mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-10 18:04:18 -05:00
Validate symbol-named properties against symbol index signatures (#44815)
* Validate symbols against both symbol and string index signatures * Add tests * Accept new baselines
This commit is contained in:
@@ -12106,7 +12106,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getApplicableIndexInfoForName(type: Type, name: __String): IndexInfo | undefined {
|
||||
return getApplicableIndexInfo(type, getStringLiteralType(unescapeLeadingUnderscores(name)));
|
||||
return getApplicableIndexInfo(type, isLateBoundName(name) ? esSymbolType : getStringLiteralType(unescapeLeadingUnderscores(name)));
|
||||
}
|
||||
|
||||
// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
|
||||
@@ -27121,8 +27121,12 @@ namespace ts {
|
||||
*/
|
||||
function isKnownProperty(targetType: Type, name: __String, isComparingJsxAttributes: boolean): boolean {
|
||||
if (targetType.flags & TypeFlags.Object) {
|
||||
// For backwards compatibility a symbol-named property is satisfied by a string index signature. This
|
||||
// is incorrect and inconsistent with element access expressions, where it is an error, so eventually
|
||||
// we should remove this exception.
|
||||
if (getPropertyOfObjectType(targetType, name) ||
|
||||
getApplicableIndexInfoForName(targetType, name) ||
|
||||
isLateBoundName(name) && getIndexInfoOfType(targetType, stringType) ||
|
||||
isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
|
||||
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
|
||||
return true;
|
||||
|
||||
@@ -68,9 +68,11 @@ tests/cases/conformance/types/members/indexSignatures1.ts(281,35): error TS2322:
|
||||
Object literal may only specify known properties, and ''someKey'' does not exist in type 'PseudoDeclaration'.
|
||||
tests/cases/conformance/types/members/indexSignatures1.ts(286,7): error TS2322: Type '"two"' is not assignable to type '`/${string}`'.
|
||||
tests/cases/conformance/types/members/indexSignatures1.ts(289,7): error TS2322: Type 'number' is not assignable to type 'PathsObject'.
|
||||
tests/cases/conformance/types/members/indexSignatures1.ts(312,43): error TS2322: Type '{ [sym]: string; }' is not assignable to type '{ [key: number]: string; }'.
|
||||
Object literal may only specify known properties, and '[sym]' does not exist in type '{ [key: number]: string; }'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/members/indexSignatures1.ts (49 errors) ====
|
||||
==== tests/cases/conformance/types/members/indexSignatures1.ts (50 errors) ====
|
||||
// Symbol index signature checking
|
||||
|
||||
const sym = Symbol();
|
||||
@@ -488,4 +490,21 @@ tests/cases/conformance/types/members/indexSignatures1.ts(289,7): error TS2322:
|
||||
const a: A = { [id]: 'test' }
|
||||
|
||||
let aid = a[id];
|
||||
|
||||
// Repro from #44793
|
||||
|
||||
interface AA {
|
||||
a?: string;
|
||||
b?: number;
|
||||
[key: symbol]: string;
|
||||
}
|
||||
|
||||
const aa: AA = { [sym]: '123' };
|
||||
|
||||
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
|
||||
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
|
||||
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2322: Type '{ [sym]: string; }' is not assignable to type '{ [key: number]: string; }'.
|
||||
!!! error TS2322: Object literal may only specify known properties, and '[sym]' does not exist in type '{ [key: number]: string; }'.
|
||||
|
||||
@@ -297,6 +297,20 @@ type A = Record<IdType, string>;
|
||||
const a: A = { [id]: 'test' }
|
||||
|
||||
let aid = a[id];
|
||||
|
||||
// Repro from #44793
|
||||
|
||||
interface AA {
|
||||
a?: string;
|
||||
b?: number;
|
||||
[key: symbol]: string;
|
||||
}
|
||||
|
||||
const aa: AA = { [sym]: '123' };
|
||||
|
||||
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
|
||||
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
|
||||
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
|
||||
|
||||
|
||||
//// [indexSignatures1.js]
|
||||
@@ -457,6 +471,10 @@ const pathObject = 123; // Error
|
||||
const id = '0000-0000-0000-0001';
|
||||
const a = { [id]: 'test' };
|
||||
let aid = a[id];
|
||||
const aa = { [sym]: '123' };
|
||||
const obj1 = { [sym]: 'hello ' };
|
||||
const obj2 = { [sym]: 'hello ' }; // Permitted for backwards compatibility
|
||||
const obj3 = { [sym]: 'hello ' }; // Error
|
||||
|
||||
|
||||
//// [indexSignatures1.d.ts]
|
||||
@@ -627,3 +645,18 @@ declare const id: IdType;
|
||||
declare type A = Record<IdType, string>;
|
||||
declare const a: A;
|
||||
declare let aid: string;
|
||||
interface AA {
|
||||
a?: string;
|
||||
b?: number;
|
||||
[key: symbol]: string;
|
||||
}
|
||||
declare const aa: AA;
|
||||
declare const obj1: {
|
||||
[key: symbol]: string;
|
||||
};
|
||||
declare const obj2: {
|
||||
[key: string]: string;
|
||||
};
|
||||
declare const obj3: {
|
||||
[key: number]: string;
|
||||
};
|
||||
|
||||
@@ -860,3 +860,42 @@ let aid = a[id];
|
||||
>a : Symbol(a, Decl(indexSignatures1.ts, 295, 5))
|
||||
>id : Symbol(id, Decl(indexSignatures1.ts, 291, 5))
|
||||
|
||||
// Repro from #44793
|
||||
|
||||
interface AA {
|
||||
>AA : Symbol(AA, Decl(indexSignatures1.ts, 297, 16))
|
||||
|
||||
a?: string;
|
||||
>a : Symbol(AA.a, Decl(indexSignatures1.ts, 301, 14))
|
||||
|
||||
b?: number;
|
||||
>b : Symbol(AA.b, Decl(indexSignatures1.ts, 302, 15))
|
||||
|
||||
[key: symbol]: string;
|
||||
>key : Symbol(key, Decl(indexSignatures1.ts, 304, 5))
|
||||
}
|
||||
|
||||
const aa: AA = { [sym]: '123' };
|
||||
>aa : Symbol(aa, Decl(indexSignatures1.ts, 307, 5))
|
||||
>AA : Symbol(AA, Decl(indexSignatures1.ts, 297, 16))
|
||||
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 307, 16))
|
||||
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
|
||||
|
||||
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
|
||||
>obj1 : Symbol(obj1, Decl(indexSignatures1.ts, 309, 5))
|
||||
>key : Symbol(key, Decl(indexSignatures1.ts, 309, 15))
|
||||
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 309, 41))
|
||||
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
|
||||
|
||||
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
|
||||
>obj2 : Symbol(obj2, Decl(indexSignatures1.ts, 310, 5))
|
||||
>key : Symbol(key, Decl(indexSignatures1.ts, 310, 15))
|
||||
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 310, 41))
|
||||
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
|
||||
|
||||
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
|
||||
>obj3 : Symbol(obj3, Decl(indexSignatures1.ts, 311, 5))
|
||||
>key : Symbol(key, Decl(indexSignatures1.ts, 311, 15))
|
||||
>[sym] : Symbol([sym], Decl(indexSignatures1.ts, 311, 41))
|
||||
>sym : Symbol(sym, Decl(indexSignatures1.ts, 2, 5))
|
||||
|
||||
|
||||
@@ -1007,3 +1007,47 @@ let aid = a[id];
|
||||
>a : A
|
||||
>id : `${number}-${number}-${number}-${number}`
|
||||
|
||||
// Repro from #44793
|
||||
|
||||
interface AA {
|
||||
a?: string;
|
||||
>a : string | undefined
|
||||
|
||||
b?: number;
|
||||
>b : number | undefined
|
||||
|
||||
[key: symbol]: string;
|
||||
>key : symbol
|
||||
}
|
||||
|
||||
const aa: AA = { [sym]: '123' };
|
||||
>aa : AA
|
||||
>{ [sym]: '123' } : { [sym]: string; }
|
||||
>[sym] : string
|
||||
>sym : unique symbol
|
||||
>'123' : "123"
|
||||
|
||||
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
|
||||
>obj1 : { [key: symbol]: string; }
|
||||
>key : symbol
|
||||
>{ [sym]: 'hello '} : { [sym]: string; }
|
||||
>[sym] : string
|
||||
>sym : unique symbol
|
||||
>'hello ' : "hello "
|
||||
|
||||
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
|
||||
>obj2 : { [key: string]: string; }
|
||||
>key : string
|
||||
>{ [sym]: 'hello '} : { [sym]: string; }
|
||||
>[sym] : string
|
||||
>sym : unique symbol
|
||||
>'hello ' : "hello "
|
||||
|
||||
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
|
||||
>obj3 : { [key: number]: string; }
|
||||
>key : number
|
||||
>{ [sym]: 'hello '} : { [sym]: string; }
|
||||
>[sym] : string
|
||||
>sym : unique symbol
|
||||
>'hello ' : "hello "
|
||||
|
||||
|
||||
@@ -300,3 +300,17 @@ type A = Record<IdType, string>;
|
||||
const a: A = { [id]: 'test' }
|
||||
|
||||
let aid = a[id];
|
||||
|
||||
// Repro from #44793
|
||||
|
||||
interface AA {
|
||||
a?: string;
|
||||
b?: number;
|
||||
[key: symbol]: string;
|
||||
}
|
||||
|
||||
const aa: AA = { [sym]: '123' };
|
||||
|
||||
const obj1: { [key: symbol]: string } = { [sym]: 'hello '};
|
||||
const obj2: { [key: string]: string } = { [sym]: 'hello '}; // Permitted for backwards compatibility
|
||||
const obj3: { [key: number]: string } = { [sym]: 'hello '}; // Error
|
||||
|
||||
Reference in New Issue
Block a user