-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
KnownKeys<T> breaking change in 4.3.1-rc #44143
Comments
For others that run into this issue, the workaround was to replace type RemoveIndex<T> = {
[P in keyof T as string extends P
? never
: number extends P
? never
: P]: T[P];
};
type KnownKeys<T> = keyof RemoveIndex<T>; Edit: I switched to @DanielRosenwasser's version in #44143 (comment), since it's compatible with more TS versions. The workaround above caused compat issues jakearchibald/idb#223 |
Another workaround: type KnownKeys<T> = {
[K in keyof T]: string extends K ? never
: number extends K ? never
: K
} extends infer R
? R extends{[_ in keyof T]: infer U}
? U
: never
: never
; |
Yeah, looks like there's definitely a change here, and I'm not sure exactly what is the root cause. Here's what I came up with as a workaround. type KeyToKeyNoIndex<T> = {
[K in keyof T]: string extends K ? never : number extends K ? never : K;
};
type ValuesOf<T> = T extends { [K in keyof T]: infer U; } ? U : never;
type KnownKeys<T> = ValuesOf<KeyToKeyNoIndex<T>>;
interface ThingWithKeysAndIndexSignature {
[s: string]: any;
foo: unknown;
bar: unknown;
}
const demo1: KnownKeys<ThingWithKeysAndIndexSignature> = 'foo';
const demo2: KnownKeys<ThingWithKeysAndIndexSignature> = 'bar';
const demo3: KnownKeys<ThingWithKeysAndIndexSignature> = 'oopsie'; |
Possibly related to #44126 - but I don't see how that ties into this one. |
I've run into a potential bug here. Or at the very least it doesn't seem to work as I expect:
as soon as the
|
I'm no TypeScript expert, but I believe the problem here is that First, let's look at this: type SpecialValueType = 'a'|'b'|'c';
type IndexValueType = 'm'|'n'|'o';
interface ThingWithKeysAndIndexSignature {
[s: string]: IndexValueType;
blah: SpecialValueType;
toOmit: SpecialValueType;
} The compiler will complain because type SpecialValueType = 'a'|'b'|'c';
type IndexValueType = 'm'|'n'|'o'|SpecialValueType;
interface ThingWithKeysAndIndexSignature {
[s: string]: IndexValueType;
blah: SpecialValueType;
toOmit: SpecialValueType;
} Now the compiler is happy because Let's test this out: function test1(t: ThingWithKeysAndIndexSignature) {
t.blah = 'm'; // error TS2322: Type '"m"' is not assignable to type 'SpecialValueType'.
} That works as expected because So what happens if we now try to use function test2(t: Omit<ThingWithKeysAndIndexSignature, 'toOmit'>) {
t.blah = 'm'; // no error!
t.blah = 'q'; // error TS2322: Type '"q"' is not assignable to type 'IndexValueType'.
} It may be unexpected to have
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; The important part here is the const test3: keyof ThingWithKeysAndIndexSignature = true; // error TS2322: Type 'boolean' is not assignable to type 'keyof ThingWithKeysAndIndexSignature'. The error is not particularly useful, but it turns out that it is equivalent to const test4: keyof ThingWithKeysAndIndexSignature extends string|number ? true : never = true; // success! What is the type of |
The root cause is basically because |
This seems to be related to evaluation order. The original code works if you wrap the first mapped object in parens: - type KnownKeys<T> = {
+ type KnownKeys<T> = ({
[K in keyof T]: string extends K ? never : number extends K ? never : K
- } extends { [_ in keyof T]: infer U } ? U : never;
+ }) extends { [_ in keyof T]: infer U } ? U : never; Playground:
This also explains why the workaround #44143 (comment) works simply by separating the first mapped object into its own type, causing it to be evaluated first. |
@jakearchibald If I may ask, I don't know how to mentally parse Edit: Ah, I figured it out. So the parenthesized version is |
Bug Report
🔎 Search Terms
KnownKeys, never, getting known interface keys
🕗 Version & Regression Information
⏯ Playground Link
💻 Code
🙁 Actual behavior
demo
has typenever
.🙂 Expected behavior
demo
has type'foo' | 'bar'
.Other info
I'm not sure where I got
KnownKeys
from, but it appears on https://stackoverflow.com/a/51956054/123395.This issue impacted https://www.npmjs.com/package/idb, but I've already worked around the issue by using the other method on that StackOverflow post. jakearchibald/idb@e3c76a5
The text was updated successfully, but these errors were encountered: