-
-
Notifications
You must be signed in to change notification settings - Fork 575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
KeysOfUnion
: Fix assignability with keyof
#1009
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import {expectType} from 'tsd'; | ||
import type {KeysOfUnion} from '../index'; | ||
import type {KeysOfUnion, UnknownRecord} from '../index'; | ||
|
||
// When passing types that are not unions, it behaves exactly as the `keyof` operator. | ||
|
||
|
@@ -35,3 +35,52 @@ type Expected2 = 'common' | 'a' | 'b' | 'c'; | |
declare const actual2: KeysOfUnion<Example2>; | ||
|
||
expectType<Expected2>(actual2); | ||
|
||
// With property modifiers | ||
declare const actual3: KeysOfUnion<{a?: string; readonly b: number} | {a: number; b: string}>; | ||
expectType<'a' | 'b'>(actual3); | ||
|
||
// `KeysOfUnion<T>` should NOT be assignable to `keyof T` | ||
type Assignability1<T, _K extends keyof T> = unknown; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add test: type Assignability1<T extends UnknownRecord, K extends keyof T> = unknown;
type Test1<T extends UnknownRecord> = Assignability1<T, KeysOfUnion<T>>; // should error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Emiyaaaaa The test you suggested doesn’t seem to produce an error: type Assignability5<T extends UnknownRecord, _K extends keyof T> = unknown;
type Test5<T extends UnknownRecord> = Assignability5<T, KeysOfUnion<T>>; // Doesn't error However, adding slightly different constraints does produce the expected error: type Assignability5<T extends Record<string, unknown>, _K extends keyof T> = unknown;
// @ts-expect-error
type Test5<T extends Record<string, unknown>> = Assignability5<T, KeysOfUnion<T>>; // Does error type Assignability5<T extends object, _K extends keyof T> = unknown;
// @ts-expect-error
type Test5<T extends object> = Assignability5<T, KeysOfUnion<T>>; // Does error Unsure what's happening here, any ideas? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have idea right now, maybe it's a bug of Typescript I'm not sure. I'll try to look into this interesting issue later There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sindresorhus These tests don't use either type Assignability5<T extends Record<string, unknown>, _K extends keyof T> = unknown;
// @ts-expect-error
type Test5<T extends Record<string, unknown>> = Assignability5<T, KeysOfUnion<T>>; // Does error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, maybe just add your tests, and then add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup makes sense, added all the above mentioned cases and some more, refer 2629884. |
||
// @ts-expect-error | ||
type Test1<T> = Assignability1<T, KeysOfUnion<T>>; | ||
|
||
// `keyof T` should be assignable to `KeysOfUnion<T>` | ||
type Assignability2<T, _K extends KeysOfUnion<T>> = unknown; | ||
type Test2<T> = Assignability2<T, keyof T>; | ||
|
||
// `KeysOfUnion<T>` should be assignable to `PropertyKey` | ||
type Assignability3<_T, _K extends PropertyKey> = unknown; | ||
type Test3<T> = Assignability3<T, KeysOfUnion<T>>; | ||
|
||
// `PropertyKey` should NOT be assignable to `KeysOfUnion<T>` | ||
type Assignability4<T, _K extends KeysOfUnion<T>> = unknown; | ||
// @ts-expect-error | ||
type Test4<T> = Assignability4<T, PropertyKey>; | ||
|
||
// `keyof T` should be assignable to `KeysOfUnion<T>` even when `T` is constrained to `Record<string, unknown>` | ||
type Assignability5<T extends Record<string, unknown>, _K extends KeysOfUnion<T>> = unknown; | ||
type Test5<T extends Record<string, unknown>> = Assignability5<T, keyof T>; | ||
|
||
// `keyof T` should be assignable to `KeysOfUnion<T>` even when `T` is constrained to `object` | ||
type Assignability6<T extends object, _K extends KeysOfUnion<T>> = unknown; | ||
type Test6<T extends object> = Assignability6<T, keyof T>; | ||
|
||
// `keyof T` should be assignable to `KeysOfUnion<T>` even when `T` is constrained to `UnknownRecord` | ||
type Assignability7<T extends UnknownRecord, _K extends KeysOfUnion<T>> = unknown; | ||
type Test7<T extends UnknownRecord> = Assignability7<T, keyof T>; | ||
|
||
// `KeysOfUnion<T>` should NOT be assignable to `keyof T` even when `T` is constrained to `Record<string, unknown>` | ||
type Assignability8<T extends Record<string, unknown>, _K extends keyof T> = unknown; | ||
// @ts-expect-error | ||
type Test8<T extends Record<string, unknown>> = Assignability8<T, KeysOfUnion<T>>; | ||
|
||
// `KeysOfUnion<T>` should NOT be assignable to `keyof T` even when `T` is constrained to `object` | ||
type Assignability9<T extends object, _K extends keyof T> = unknown; | ||
// @ts-expect-error | ||
type Test9<T extends object> = Assignability9<T, KeysOfUnion<T>>; | ||
|
||
// `KeysOfUnion<T>` should NOT be assignable to `keyof T` even when `T` is constrained to `UnknownRecord` | ||
// type Assignability10<T extends UnknownRecord, _K extends keyof T> = unknown; | ||
// The following line should error but it doesn't, this is an issue with the existing implementation of `KeysOfUnion` | ||
// type Test10<T extends UnknownRecord> = Assignability10<T, KeysOfUnion<T>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way not hack?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be, but I couldn't figure out anything else.
Do you have any ideas or suggestions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to do this for now until we come up with a better solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I agree!
And this is probably a bug, refer this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, removed intersection with
KeysOfUnions
as it's not required after this fix, refer db0ce32.