-
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
number should be subtype of keyof T[] #13715
Comments
At runtime, any index is really a string. so |
The following example is mentioned in function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]; // Inferred type is T[K]
} It does not work to access elements of getProperty(array, 1) // Type '1' is not assignable to type '"length" | "toString" | "toLocaleString" | "push" | "pop" | "concat" | "join" | "reverse" | "shif...'. You can solve this by overloading: function getProperty<E>(obj: E[], key: number): E
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K]
function getProperty(obj: any, key: any): any {
return obj[key]
} So I just did what you suggested:
However, this does not work, if interface Wrapper<T> {
getProperty<K extends keyof T>(key: K): Wrapper<T[K]>
} If interface Wrapper<T> {
// the overload in the next line should only be applied if T is an array
getProperty(key: number): any // return type is any since you do not know the element type of T
getProperty<K extends keyof T>(key: K): Wrapper<T[K]>
} If |
Moreover, it seems inconsequent to me that |
We just hit this limitation ourselves. We're filtering some command objects that have their type identified by a numeric ID and were hoping to have them properly typed in our filter method. This is the workaround we have right now, but as you can see it's ugly. We're passing the ID in as a string instead of a number (yuck) and then converting every command's ID to string to compare it, also yuck. If numbers were supported in keyof as well as strings, this would just work. interface Commands {
[id: number]: IncomingCommand;
20: IncomingCommand & {
commandTwentySpecificValue: number
}
}
function on<TId extends keyof Commands>(id: TId, f: (command: Commands[TId]) => void){
// Calls f for every command that passes through where potentialCommand.id.toString() === id
}
on("20", command => {
console.log(command.commandTwentySpecificValue);
}); |
What about adding an overload like this? function getProperty<T>(obj: { [index: number]: T }, key: number): T It wouldn't fix all your problems, but it'd work in certain places. |
Faced with this when tried to improve immutablejs methods with typesafety. interface Record<T> {
setIn<K1 extends keyof T>(k: [K1], v: T[K1]): void;
setIn<K1 extends keyof T, K2 extends keyof T[K1]>(k: [K1, K2], v: T[K1][K2]): void;
setIn<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2]>(k: [K1, K2, K3], v: T[K1][K2][K3]): void;
setIn<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3]>(k: [K1, K2, K3, K4], v: T[K1][K2][K3][K4]): void;
setIn<K1 extends keyof T, K2 extends keyof T[K1], K3 extends keyof T[K1][K2], K4 extends keyof T[K1][K2][K3], K5 extends keyof T[K1][K2][K3][K4]>(k: [K1, K2, K3, K4, K5], v: T[K1][K2][K3][K4][K5]): void;
} This works nicely until T is nested object. When it'd be Array or Map - keyof can't detect keytype of it. Maybe solution could be ability to explicitly declare keyof. E.g. type MyArray = {
[keyof]: number
} Also saw you are planning making variadic generics. Will they help solving this problem? |
Why was this closed? Should we take it you never intend to support this feature? Does this count as wont-fix or working-as-intended? |
The issue was auto-closed since there was no clear action. and has not been touched in more than 14 days. As noted earlier, number does not exist at runtime, only strings. a solution would be to add a new primitive type |
@mhegazy Please reopen. In my opinion, this discussion is not done. I guess @goloveychuk and @LennyLixalot do agree. No one reacted to my counter arguments: They are approved by use cases of @goloveychuk and @LennyLixalot. @goloveychuk's use case refers to the current design limitation regarding generic type variables of an object and @LennyLixalot's use case refers to the mentioned design discrepancy. My suggestion would solve both use cases. The only argument for the current design decision is
I still don't get, why at runtime is a design factor in this case.
If at runtime is crucial, indexing into an array using
interface Container<T> {
[index: number]: T
} @mhegazy Please elaborate on this. Why should at runtime still be a design factor in this case, but not in others? |
Yes, we agree that something should be done about this, it's overly restrictive and has forced us to make a few sub-par design decisions to maintain strong-typing. |
This how the feature was implemented originally, and we ran into other issues and had to be restricted to a subtype of string. see more details in #12425. So unless there is a new proposal on how to mitigate these issues but still support indexing with numbers, not sure reopening this issue adds much. |
As far as I've seen, all those issues are related to instantiated types, that can not be typed using
They are no valid use case of What
|
I think so :). |
interface Thing {
name: string;
width: number;
height: number;
inStock: boolean;
}
type K1 = keyof Thing; // "name" | "width" | "height" | "inStock"
type I1 = indexof Thing; // never
type K2 = keyof Thing[]; // "length" | "push" | "pop" | "concat" | ...
type I2 = indexof Thing[]; // number
type K3 = keyof { [x: string]: Thing }; // string
type I3 = indexof { [x: string]: Thing }; // number
type K4 = keyof { [x: number]: Thing }; // never
type I4 = indexof { [x: number]: Thing }; // number
type K5 = keyof { 0: Thing, "1": Thing }; // "0" | "1"
type I5 = indexof { 0: Thing, "1": Thing }; // 0 | 1 |
interface Wrapper<T> {
getProperty<K extends (keyof T | indexof T)>(key: K): Wrapper<T[K]>
} |
Keys are converted as string internally. microsoft/TypeScript#13715
TypeScript Version: 2.1.5
Code
Expected behavior:
number
should be subtype ofkeyof string[]
andkeyof Container<string>
Actual behavior:
number
is no subtype ofkeyof string[]
andkeyof Container<string>
The text was updated successfully, but these errors were encountered: