-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Storing a keyof to a string quoted number key produces TS2344: invalid constraint in declaration file #37292
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
// test.ts
const x = {
'1': 1,
'2a': 1,
}; // test.d.ts
declare const x: {
1: number;
'2a': number;
}; // test.js
const x = {
'1': 1,
'2a': 1,
}; Which the right property name should be in the test.d.ts? I noticed that TS converts property name from function createPropertyNameNodeForIdentifierOrLiteral(name: string, singleQuote?: boolean) {
return isIdentifierText(name, compilerOptions.target) ? createIdentifier(name) :
createLiteral(isNumericLiteralName(name) ? +name : name, !!singleQuote);
} function isNumericLiteralName(name: string | __String) {
return (+name).toString() === name;
} |
@ahejlsberg we could use some feedback here. This is pretty messy. Our type system sort of traffics around whether property keys were quoted or not, and this observable effects in .d.ts emit and assignability when declare function getKeys<T>(x: T): keyof T;
// Both .d.ts emit as { 1: string }
const unquo = { 1: 'a' };
const quotd = { '1': 'a' };
// They are cross-assignable
let q1: typeof unquo = quotd;
let q2: typeof quotd = unquo;
// Error
let e1: keyof typeof unquo = null as any as keyof typeof quotd;
// Error
let e2: keyof typeof quotd = null as any as keyof typeof unquo;
// .d.ts emit here differs:
// 1
const unquoKey = getKeys(unquo);
// "1"
const quotdKey = getKeys(quotd); Basically, the object type printback for an object will always "normalize" numeric-like property keys to their unquoted form, but // .d.ts emit is { 1: string, 2: string }
const mixer = { 1: 'a', "2": 'a' };
class X<K extends keyof typeof mixer> {
constructor(obj: K) { }
}
// OK in original, OK in .d.ts
const unquoInst = new X(1);
// OK in original, error in .d.ts because "2" is not 2
const quotdInst = new X("2"); I don't know how we touch this without badly breaking "manual tuple" scenarios like |
I'm thinking that object types with numerically named properties (whether declared with quotes or without quotes) should have their |
I've thought the same, since they resolve to the same slot - the issue is really mapped types; when you feed both of those into a mapped type an get two differing property types back for '1' vs 1, you're gunna have a bad time. |
@weswigham We recently merged #39101 which addresses a similar situation where members from distinct enum types have the same underlying numeric value. We now union together the results of the individual mapping operations. An alternative would be to run the mapping operation just once for each slot and have the iterated key type be a union of those key values that resolve to the slot. But either way, there may not be an issue with mapped types. |
Of course the simpler fix is to just preserve the spelling of property names in declaration files. I'm putting up a PR for that. |
TypeScript Version: 3.8.3 and 3.9.0-dev.20200309
Search Terms: [keyof quote], [keyof number declaration], [key number], [key quote], [object key number declaration]
Code
Expected behavior:
tsc --declaration test.ts
npx tsc --noEmit test.d.ts
Actual behavior:
Step (2) actually produces an error:
npx tsc --noEmit test.d.ts
When examining the output, note the two lines marked "BUG": the key in the
lines
object for1
has been converted to number, but theRef<"1">
is trying to access the key as a string.When tsc un-quotes the numeric key in
C
's definition, theRef<"1">
produces a type mismatch.Playground Link: Playground Link
Related Issues:
The text was updated successfully, but these errors were encountered: