-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
mapped type with remapped keys unexpectedly widens type #57265
Comments
It changed in |
The UX problem that we can recognize here is that all of those 3 display in the same way ( type A = { [k: string]: unknown }
type B = Record<string, unknown>
type C = { [K in keyof B as K]: unknown } However, they are not functionally identical and that's the surprising behavior. |
Yes there is a UX problem here that two different types are presented the same and behave differently, and that's definitely a problem. It's still a mystery to me is WHY the key gets widened in the first place from
The keys of an object are never numbers - they're always strings. Property access by number coerces the number to a string, both at the type level and at the value level. Why does I think the discussion at #41966 bears on this, and I wonder if #43041 (An alternative option to Here's my trying to understand: // @target: ES2022
const r = {0:'zero', 1:'one', 2:'two'}
// two similar record types
type R1 = Record<string, string>
type R2 = {[s:string]:string}
// both types are indexable by number
const v1 = (r as R1)[0]
const v2 = (r as R2)[0]
// and have values indexable by number
type V1 = R1['0']
// ^?
type V2 = R2['0']
// ^?
// and for-in types the iterated keys to be strings
for (const k in (r as R1)){}
// ^?
for (const k in (r as R2)){}
// ^?
// but `keyof` differs between the types:
type K1 = keyof R1
// ^?
type K2 = keyof R2
// ^? |
This is true, but note they're not completely unified at the type level - types can have separate |
That's one way to think of it. But I don't think it really makes sense.
// @target:ES2022
declare const o : {[x:number]:'number'}&{[x:string]:'string'}&{[x:`${number}`]:'numstring'}
const p1 = o[1]
// ^?
const p2 = o['1']
// ^?
const p3 = o['one']
// ^?
const p4 = o['0x0']
// ^?
const p5 = o['Infinity']
// ^?
const p6 = o[`${300n}`]
// ^?
const p7 = o['123_456']
// ^?
const p8 = o[123_456]
// ^?
const p9 = o['-1']
// ^?
const p10 = o['+1']
// ^?
const p11 = o[`${[6]}`]
// ^? Workbench Repro |
Fun little technicality there: Those pathological cases actually only exist because of a design limitation. The type |
The inconsistency does not require use of intersection types to see: declare const o : {
[x:string]:'string'|'number'|'numstring',
[x:number]:'number'|'numstring',
[x:`${number}`]:'numstring'
}
// rest stay the same And that design limitation does not excuse/explain the behavior. TypeScript could treat the type as |
π Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript. Issue body code block by @rotu β Failed: -
Historical Information
|
π Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.
Historical Information
|
π Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of this repro running against the nightly TypeScript.
Historical Information
|
I am currently solving this problem temporarily by defining a type Keys<T> = { [P in keyof T as any]: P }[keyof T]
type R1 = Keys<Record<string,unknown>> // string
type R2 = Keys<{ [x: string]: unknown }> // string |
π Search Terms
keyof, string, record
π Version & Regression Information
β― Playground Link
No response
π» Code
Workbench Repro
π Actual behavior
keyof R
isstring | number
.π Expected behavior
keyof R
isstring
.Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: