-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Numeric template type can't be used to index tuple in mapped type (despite actually working anyway) #48739
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
Comments
Actually I might close this and open an issue on a more basic failure in mapped types, the fact that constraints don't work despite mapped types ignoring methods in array/tuple types: type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[K in keyof Type]: Local<Type[K]>
}; |
As the other issue is supposedly a working as intended, I'm gonna reopen this one. |
I guess what I'm not understanding is why you don't write this type type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[K in number & keyof Type]: Local<Type[K]>;
}; which seems to do what you're looking for? Adding more ad-hoc rules around e.g. |
The problem with that is it deletes all the other properties (and without negated types, we can't do const locals: LocalsFor<[I32, Externref]> = [
new Local(new I32()),
new Local(new Externref()),
];
// Property 'length' does not exist on type 'LocalsFor<[I32, Externref]>'.
console.log(locals.length);
This wouldn't really be ad-hoc, like type CheckNumKeys<Type extends ReadonlyArray<any>> = {
[K in keyof Type]: K extends number ? "key-is-number" : "key-not-number"
};
// ["key-not-number", "key-not-number"]
type X = CheckNums<["a", "b"]>; Like why would |
Also just to be clear, the example in the initial comment almost works, like the type is mapped just fine (as As you've pointed out in the previous issue, the keys aren't exposed as numbers, because well they aren't numbers. However neither are |
Okay so funnily enough, just adding the signature myself actually works: declare global {
interface ReadonlyArray<T> {
[k: `${ number }`]: T;
}
} This should probably still be in the standard types that TypeScript ships as this is, well, how arrays in JS work. But at least I have an actually good workaround now. |
No, because We're still left with the problem identified in the other issue type M = ReadonlyArray<ValType> & { "0.2": boolean };
type G = LocalsFor<M>;
type X = G["0.2"]; // Local<boolean>, a constraint violation WLOG (constrained to your scenario where presumably you don't care about that example, which is super valid) you can write type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[K in keyof Type]: Local<Type[K] & ValType>;
}; or type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[K in keyof Type]: Type[K] extends ValType ? Local<Type[K]> : never;
}; depending on what you want to happen to out-of-band values. |
I mean given that TypeScript already behaves like this for the arrays: const arr = [1,2,3];
// x is type number
const x = arr[0.2] It wouldn't be that inconsistent. If people really want to attach non(positive)-integer numbers to arrays, they already do so at their own peril today anyway. Like if TypeScript ever fixed
This one suffers the problem of deleting types again, although even if it's changed to: type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[K in keyof Type]: Type[K] extends ValType ? Local<Type[K]> : Type[K];
}; It just breaks the type in ways I'm not entirely clear on, and I'm not sure what a minimal reproduction actually is: I suspect it has to do with the extra indirection through more generics, my suggested fix doesn't suffer this problem though and instead there is no type error there, so the actual array parts are preserved properly with my fix.
This suffers exactly the same problem as the previous suggestion.
I'm not clear why this is the case, adding the signature I suggested just above would basically be consistent with how TypeScript already treats |
The cure has to be better than the disease. Adding that declaration would permit things like declare const arr: ReadonlyArray<boolean>;
// j: boolean
let j = arr["4.0"]; I'm calling this unactionable because not liking how mapped type are/aren't homomorhpic or do/don't deal with numeric-like keys isn't something we can change in a way that doesn't cause someone else to not like it instead, with the added drawback of being a breaking change. |
Why were int types rejected as a suggestion then? Because if This is something that has been frustrating me with TypeScript for quite a long time now, and that's that there is a lot of historical baggage that has fixes outright rejected (like the aforementioned It's a shame because there really just aren't major competitors to TypeScript (Flow doesn't really compete in the same space, as it's more a subset of JS that is strictly typed, rather than adding types to JS, and they announced a fairly significant direction change recently to non-standard JS features). There is one ridiculously excellent type system in Hegel, however it lacks one main feature which is why I've stayed with TypeScript and that's suggestions/completions. |
By the way... using The problem here is that the result of this produces an object type with an index signature. If we really want this then there is no point in using a mapped type here at all because we will end up with a single key~ anyway. This could be rewritten to: type LocalsFor<Type extends ReadonlyArray<ValType>> = {
[x: number]: Local<Type[number]>
} TS playground with the code above So it seems that a better approach is to actually use That on the other hand... works somewhat accidentally. The problem in here is that when mapping over a tuple we'll get keys like The problem is... this still doesn't really work as expected. Because we've accessed an index signature we got a union back of all possible value types from this tuple and most likely the intention was to map each position separately. It is possible to type this index by index by for what seems to be a pretty standard operation... it's pretty complex and requires using nested conditional types or the new I'm also discussing this problem in the comments here: #48599 |
@Jamesernator @RyanCavanaugh I believe that the issue here was addressed by #48837 |
This issue has been marked as 'Unactionable' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Bug Report
🔎 Search Terms
numeric string, number template, tuple
🕗 Version & Regression Information
Note
This is a followup to another issue I raised where similar issues arose, however here the exact cause is different and more obviously a bug rather than intended behaviour.
This might still be a design limitation, but it's unclear to me how, without this, one can map a tuple type to something with a generic constraint.
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
It produces an error, despite the fact
K extends `${ number }`
should be treated as a tuple key. (As per the other issue I raised numbers can't be used to index tuples, so a numeric string is the only way to distinguish tuple entry keys from methods for mapping tuple types).The final bit of code, shows that the constraint does work, however it's not treated as a valid key for
ReadonlyArray<ValType>
despite subtypes being potentially tuples.Note that because of the type constraint, mapped tuple types do not work.
🙂 Expected behavior
This should not have an error, as it otherwise works and the constraint should filter the tuple member keys (and not the methods).
The text was updated successfully, but these errors were encountered: