From 6e0bcd91a670f430bea8ff282b8dce572e449de1 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 4 Apr 2018 11:53:00 -0700 Subject: [PATCH 1/3] Only include unique symbols when getting index types for access checks --- src/compiler/checker.ts | 40 ++++++--- src/compiler/types.ts | 6 ++ .../keyofDoesntContainSymbols.errors.txt | 29 +++++++ .../reference/keyofDoesntContainSymbols.js | 35 ++++++++ .../keyofDoesntContainSymbols.symbols | 72 ++++++++++++++++ .../reference/keyofDoesntContainSymbols.types | 85 +++++++++++++++++++ .../compiler/keyofDoesntContainSymbols.ts | 21 +++++ 7 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/keyofDoesntContainSymbols.errors.txt create mode 100644 tests/baselines/reference/keyofDoesntContainSymbols.js create mode 100644 tests/baselines/reference/keyofDoesntContainSymbols.symbols create mode 100644 tests/baselines/reference/keyofDoesntContainSymbols.types create mode 100644 tests/cases/compiler/keyofDoesntContainSymbols.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 07801f5b5bc92..7b2bb5d72551c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8109,12 +8109,16 @@ namespace ts { return links.resolvedType; } - function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType) { - if (!type.resolvedIndexType) { - type.resolvedIndexType = createType(TypeFlags.Index); - type.resolvedIndexType.type = type; + function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, includeSymbols?: boolean) { + const cacheLocation = includeSymbols ? "resolvedDeclaredIndexType" : "resolvedIndexType"; + if (!type[cacheLocation]) { + type[cacheLocation] = createType(TypeFlags.Index); + type[cacheLocation].type = type; + if (includeSymbols) { + type[cacheLocation].isDeclaredType = true; + } } - return type.resolvedIndexType; + return type[cacheLocation]; } function getLiteralTypeFromPropertyName(prop: Symbol) { @@ -8132,17 +8136,27 @@ namespace ts { return links.nameType; } - function getLiteralTypeFromPropertyNames(type: Type) { - return getUnionType(map(getPropertiesOfType(type), getLiteralTypeFromPropertyName)); + function isTypeNotSymbol(type: Type) { + return !isTypeAssignableToKind(type, TypeFlags.ESSymbolLike); + } + + function getLiteralTypeFromPropertyNames(type: Type, includeSymbols?: boolean) { + const originalKeys = map(getPropertiesOfType(type), getLiteralTypeFromPropertyName); + if (includeSymbols) { + return getUnionType(originalKeys); + } + else { + return getUnionType(filter(originalKeys, isTypeNotSymbol)); + } } - function getIndexType(type: Type): Type { - return type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t))) : - maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(type) : + function getIndexType(type: Type, includeSymbols?: boolean): Type { + return type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t, includeSymbols))) : + maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(type, includeSymbols) : getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(type) : type === wildcardType ? wildcardType : type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType : - getLiteralTypeFromPropertyNames(type); + getLiteralTypeFromPropertyNames(type, includeSymbols); } function getIndexTypeOrString(type: Type): Type { @@ -10318,7 +10332,7 @@ namespace ts { // constraint of T. const constraint = getConstraintForRelation((target).type); if (constraint) { - if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) { + if (result = isRelatedTo(source, getIndexType(constraint, (target as IndexType).isDeclaredType), reportErrors)) { return result; } } @@ -20840,7 +20854,7 @@ namespace ts { // Check if the index type is assignable to 'keyof T' for the object type. const objectType = (type).objectType; const indexType = (type).indexType; - if (isTypeAssignableTo(indexType, getIndexType(objectType))) { + if (isTypeAssignableTo(indexType, getIndexType(objectType, /*includeSymbols*/ true))) { if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType) & MappedTypeModifiers.IncludeReadonly) { error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 63829b0fa9aef..a526ecc5dff98 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3753,6 +3753,8 @@ namespace ts { /* @internal */ resolvedIndexType: IndexType; /* @internal */ + resolvedDeclaredIndexType: IndexType; + /* @internal */ resolvedBaseConstraint: Type; /* @internal */ couldContainTypeVariables: boolean; @@ -3839,6 +3841,8 @@ namespace ts { resolvedBaseConstraint?: Type; /* @internal */ resolvedIndexType?: IndexType; + /* @internal */ + resolvedDeclaredIndexType?: IndexType; } // Type parameters (TypeFlags.TypeParameter) @@ -3870,6 +3874,8 @@ namespace ts { // keyof T types (TypeFlags.Index) export interface IndexType extends InstantiableType { + /* @internal */ + isDeclaredType?: boolean; type: InstantiableType | UnionOrIntersectionType; } diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt b/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt new file mode 100644 index 0000000000000..d362e20f01f81 --- /dev/null +++ b/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/keyofDoesntContainSymbols.ts(10,30): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. +tests/cases/compiler/keyofDoesntContainSymbols.ts(13,23): error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'. + + +==== tests/cases/compiler/keyofDoesntContainSymbols.ts (2 errors) ==== + const sym = Symbol(); + const obj = { num: 0, str: 's', [sym]: sym }; + + function set (obj: T, key: K, value: T[K]): T[K] { + return obj[key] = value; + } + + const val = set(obj, 'str', ''); + // string + const valB = set(obj, 'num', ''); + ~~ +!!! error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. + // Expect type error + // Argument of type '""' is not assignable to parameter of type 'number'. + const valC = set(obj, sym, sym); + ~~~ +!!! error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'. + // Expect type error + // Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" + type KeyofObj = keyof typeof obj; + // "str" | "num" + type Values = T[keyof T]; + + type ValuesOfObj = Values; \ No newline at end of file diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.js b/tests/baselines/reference/keyofDoesntContainSymbols.js new file mode 100644 index 0000000000000..d6452601f2c0b --- /dev/null +++ b/tests/baselines/reference/keyofDoesntContainSymbols.js @@ -0,0 +1,35 @@ +//// [keyofDoesntContainSymbols.ts] +const sym = Symbol(); +const obj = { num: 0, str: 's', [sym]: sym }; + +function set (obj: T, key: K, value: T[K]): T[K] { + return obj[key] = value; +} + +const val = set(obj, 'str', ''); +// string +const valB = set(obj, 'num', ''); +// Expect type error +// Argument of type '""' is not assignable to parameter of type 'number'. +const valC = set(obj, sym, sym); +// Expect type error +// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +type KeyofObj = keyof typeof obj; +// "str" | "num" +type Values = T[keyof T]; + +type ValuesOfObj = Values; + +//// [keyofDoesntContainSymbols.js] +var sym = Symbol(); +var obj = (_a = { num: 0, str: 's' }, _a[sym] = sym, _a); +function set(obj, key, value) { + return obj[key] = value; +} +var val = set(obj, 'str', ''); +// string +var valB = set(obj, 'num', ''); +// Expect type error +// Argument of type '""' is not assignable to parameter of type 'number'. +var valC = set(obj, sym, sym); +var _a; diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.symbols b/tests/baselines/reference/keyofDoesntContainSymbols.symbols new file mode 100644 index 0000000000000..5901e76295e88 --- /dev/null +++ b/tests/baselines/reference/keyofDoesntContainSymbols.symbols @@ -0,0 +1,72 @@ +=== tests/cases/compiler/keyofDoesntContainSymbols.ts === +const sym = Symbol(); +>sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --)) + +const obj = { num: 0, str: 's', [sym]: sym }; +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 13)) +>str : Symbol(str, Decl(keyofDoesntContainSymbols.ts, 1, 21)) +>[sym] : Symbol([sym], Decl(keyofDoesntContainSymbols.ts, 1, 31)) +>sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) +>sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) + +function set (obj: T, key: K, value: T[K]): T[K] { +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 3, 52)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) +>key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 3, 59)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) +>value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 3, 67)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) + + return obj[key] = value; +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 3, 52)) +>key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 3, 59)) +>value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 3, 67)) +} + +const val = set(obj, 'str', ''); +>val : Symbol(val, Decl(keyofDoesntContainSymbols.ts, 7, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + +// string +const valB = set(obj, 'num', ''); +>valB : Symbol(valB, Decl(keyofDoesntContainSymbols.ts, 9, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + +// Expect type error +// Argument of type '""' is not assignable to parameter of type 'number'. +const valC = set(obj, sym, sym); +>valC : Symbol(valC, Decl(keyofDoesntContainSymbols.ts, 12, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) +>sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) + +// Expect type error +// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +type KeyofObj = keyof typeof obj; +>KeyofObj : Symbol(KeyofObj, Decl(keyofDoesntContainSymbols.ts, 12, 32)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + +// "str" | "num" +type Values = T[keyof T]; +>Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 15, 33)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) + +type ValuesOfObj = Values; +>ValuesOfObj : Symbol(ValuesOfObj, Decl(keyofDoesntContainSymbols.ts, 17, 28)) +>Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 15, 33)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.types b/tests/baselines/reference/keyofDoesntContainSymbols.types new file mode 100644 index 0000000000000..f2bd8e3568909 --- /dev/null +++ b/tests/baselines/reference/keyofDoesntContainSymbols.types @@ -0,0 +1,85 @@ +=== tests/cases/compiler/keyofDoesntContainSymbols.ts === +const sym = Symbol(); +>sym : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +const obj = { num: 0, str: 's', [sym]: sym }; +>obj : { num: number; str: string; [sym]: symbol; } +>{ num: 0, str: 's', [sym]: sym } : { num: number; str: string; [sym]: symbol; } +>num : number +>0 : 0 +>str : string +>'s' : "s" +>[sym] : symbol +>sym : unique symbol +>sym : unique symbol + +function set (obj: T, key: K, value: T[K]): T[K] { +>set : (obj: T, key: K, value: T[K]) => T[K] +>T : T +>K : K +>T : T +>obj : T +>T : T +>key : K +>K : K +>value : T[K] +>T : T +>K : K +>T : T +>K : K + + return obj[key] = value; +>obj[key] = value : T[K] +>obj[key] : T[K] +>obj : T +>key : K +>value : T[K] +} + +const val = set(obj, 'str', ''); +>val : string +>set(obj, 'str', '') : string +>set : (obj: T, key: K, value: T[K]) => T[K] +>obj : { num: number; str: string; [sym]: symbol; } +>'str' : "str" +>'' : "" + +// string +const valB = set(obj, 'num', ''); +>valB : any +>set(obj, 'num', '') : any +>set : (obj: T, key: K, value: T[K]) => T[K] +>obj : { num: number; str: string; [sym]: symbol; } +>'num' : "num" +>'' : "" + +// Expect type error +// Argument of type '""' is not assignable to parameter of type 'number'. +const valC = set(obj, sym, sym); +>valC : any +>set(obj, sym, sym) : any +>set : (obj: T, key: K, value: T[K]) => T[K] +>obj : { num: number; str: string; [sym]: symbol; } +>sym : unique symbol +>sym : unique symbol + +// Expect type error +// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +type KeyofObj = keyof typeof obj; +>KeyofObj : "str" | "num" +>obj : { num: number; str: string; [sym]: symbol; } + +// "str" | "num" +type Values = T[keyof T]; +>Values : T[keyof T] +>T : T +>T : T +>T : T + +type ValuesOfObj = Values; +>ValuesOfObj : string | number +>Values : T[keyof T] +>obj : { num: number; str: string; [sym]: symbol; } + diff --git a/tests/cases/compiler/keyofDoesntContainSymbols.ts b/tests/cases/compiler/keyofDoesntContainSymbols.ts new file mode 100644 index 0000000000000..991a7a97e4eab --- /dev/null +++ b/tests/cases/compiler/keyofDoesntContainSymbols.ts @@ -0,0 +1,21 @@ +// @lib: es6 +const sym = Symbol(); +const obj = { num: 0, str: 's', [sym]: sym }; + +function set (obj: T, key: K, value: T[K]): T[K] { + return obj[key] = value; +} + +const val = set(obj, 'str', ''); +// string +const valB = set(obj, 'num', ''); +// Expect type error +// Argument of type '""' is not assignable to parameter of type 'number'. +const valC = set(obj, sym, sym); +// Expect type error +// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +type KeyofObj = keyof typeof obj; +// "str" | "num" +type Values = T[keyof T]; + +type ValuesOfObj = Values; \ No newline at end of file From 216f1133ebea81c33661c8cd969ab2a382fc58f9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 4 Apr 2018 16:06:48 -0700 Subject: [PATCH 2/3] Filter all nonstrings --- src/compiler/checker.ts | 26 ++--- .../keyofDoesntContainSymbols.errors.txt | 17 +++- .../reference/keyofDoesntContainSymbols.js | 14 ++- .../keyofDoesntContainSymbols.symbols | 95 +++++++++++-------- .../reference/keyofDoesntContainSymbols.types | 36 +++++-- .../compiler/keyofDoesntContainSymbols.ts | 8 +- 6 files changed, 124 insertions(+), 72 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7b2bb5d72551c..8222ff0f205df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8109,12 +8109,12 @@ namespace ts { return links.resolvedType; } - function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, includeSymbols?: boolean) { - const cacheLocation = includeSymbols ? "resolvedDeclaredIndexType" : "resolvedIndexType"; + function getIndexTypeForGenericType(type: InstantiableType | UnionOrIntersectionType, includeDeclaredTypes?: boolean) { + const cacheLocation = includeDeclaredTypes ? "resolvedDeclaredIndexType" : "resolvedIndexType"; if (!type[cacheLocation]) { type[cacheLocation] = createType(TypeFlags.Index); type[cacheLocation].type = type; - if (includeSymbols) { + if (includeDeclaredTypes) { type[cacheLocation].isDeclaredType = true; } } @@ -8136,27 +8136,27 @@ namespace ts { return links.nameType; } - function isTypeNotSymbol(type: Type) { - return !isTypeAssignableToKind(type, TypeFlags.ESSymbolLike); + function isTypeString(type: Type) { + return isTypeAssignableToKind(type, TypeFlags.StringLike); } - function getLiteralTypeFromPropertyNames(type: Type, includeSymbols?: boolean) { + function getLiteralTypeFromPropertyNames(type: Type, includeDeclaredTypes?: boolean) { const originalKeys = map(getPropertiesOfType(type), getLiteralTypeFromPropertyName); - if (includeSymbols) { + if (includeDeclaredTypes) { return getUnionType(originalKeys); } else { - return getUnionType(filter(originalKeys, isTypeNotSymbol)); + return getUnionType(filter(originalKeys, isTypeString)); } } - function getIndexType(type: Type, includeSymbols?: boolean): Type { - return type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t, includeSymbols))) : - maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(type, includeSymbols) : + function getIndexType(type: Type, includeDeclaredTypes?: boolean): Type { + return type.flags & TypeFlags.Intersection ? getUnionType(map((type).types, t => getIndexType(t, includeDeclaredTypes))) : + maybeTypeOfKind(type, TypeFlags.InstantiableNonPrimitive) ? getIndexTypeForGenericType(type, includeDeclaredTypes) : getObjectFlags(type) & ObjectFlags.Mapped ? getConstraintTypeFromMappedType(type) : type === wildcardType ? wildcardType : type.flags & TypeFlags.Any || getIndexInfoOfType(type, IndexKind.String) ? stringType : - getLiteralTypeFromPropertyNames(type, includeSymbols); + getLiteralTypeFromPropertyNames(type, includeDeclaredTypes); } function getIndexTypeOrString(type: Type): Type { @@ -20854,7 +20854,7 @@ namespace ts { // Check if the index type is assignable to 'keyof T' for the object type. const objectType = (type).objectType; const indexType = (type).indexType; - if (isTypeAssignableTo(indexType, getIndexType(objectType, /*includeSymbols*/ true))) { + if (isTypeAssignableTo(indexType, getIndexType(objectType, /*includeDeclaredTypes*/ true))) { if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && getObjectFlags(objectType) & ObjectFlags.Mapped && getMappedTypeModifiers(objectType) & MappedTypeModifiers.IncludeReadonly) { error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt b/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt index d362e20f01f81..f87631f9dbae1 100644 --- a/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt +++ b/tests/baselines/reference/keyofDoesntContainSymbols.errors.txt @@ -1,10 +1,12 @@ -tests/cases/compiler/keyofDoesntContainSymbols.ts(10,30): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. -tests/cases/compiler/keyofDoesntContainSymbols.ts(13,23): error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'. +tests/cases/compiler/keyofDoesntContainSymbols.ts(11,30): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. +tests/cases/compiler/keyofDoesntContainSymbols.ts(14,23): error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'. +tests/cases/compiler/keyofDoesntContainSymbols.ts(17,23): error TS2345: Argument of type '0' is not assignable to parameter of type '"str" | "num"'. -==== tests/cases/compiler/keyofDoesntContainSymbols.ts (2 errors) ==== +==== tests/cases/compiler/keyofDoesntContainSymbols.ts (3 errors) ==== const sym = Symbol(); - const obj = { num: 0, str: 's', [sym]: sym }; + const num = 0; + const obj = { num: 0, str: 's', [num]: num as 0, [sym]: sym }; function set (obj: T, key: K, value: T[K]): T[K] { return obj[key] = value; @@ -21,7 +23,12 @@ tests/cases/compiler/keyofDoesntContainSymbols.ts(13,23): error TS2345: Argument ~~~ !!! error TS2345: Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num"'. // Expect type error - // Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" + // Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" + const valD = set(obj, num, num); + ~~~ +!!! error TS2345: Argument of type '0' is not assignable to parameter of type '"str" | "num"'. + // Expect type error + // Argument of type '0' is not assignable to parameter of type "str" | "num" type KeyofObj = keyof typeof obj; // "str" | "num" type Values = T[keyof T]; diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.js b/tests/baselines/reference/keyofDoesntContainSymbols.js index d6452601f2c0b..ad965aae1e4a5 100644 --- a/tests/baselines/reference/keyofDoesntContainSymbols.js +++ b/tests/baselines/reference/keyofDoesntContainSymbols.js @@ -1,6 +1,7 @@ //// [keyofDoesntContainSymbols.ts] const sym = Symbol(); -const obj = { num: 0, str: 's', [sym]: sym }; +const num = 0; +const obj = { num: 0, str: 's', [num]: num as 0, [sym]: sym }; function set (obj: T, key: K, value: T[K]): T[K] { return obj[key] = value; @@ -13,7 +14,10 @@ const valB = set(obj, 'num', ''); // Argument of type '""' is not assignable to parameter of type 'number'. const valC = set(obj, sym, sym); // Expect type error -// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +// Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" +const valD = set(obj, num, num); +// Expect type error +// Argument of type '0' is not assignable to parameter of type "str" | "num" type KeyofObj = keyof typeof obj; // "str" | "num" type Values = T[keyof T]; @@ -22,7 +26,8 @@ type ValuesOfObj = Values; //// [keyofDoesntContainSymbols.js] var sym = Symbol(); -var obj = (_a = { num: 0, str: 's' }, _a[sym] = sym, _a); +var num = 0; +var obj = (_a = { num: 0, str: 's' }, _a[num] = num, _a[sym] = sym, _a); function set(obj, key, value) { return obj[key] = value; } @@ -32,4 +37,7 @@ var valB = set(obj, 'num', ''); // Expect type error // Argument of type '""' is not assignable to parameter of type 'number'. var valC = set(obj, sym, sym); +// Expect type error +// Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" +var valD = set(obj, num, num); var _a; diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.symbols b/tests/baselines/reference/keyofDoesntContainSymbols.symbols index 5901e76295e88..31f208f40631f 100644 --- a/tests/baselines/reference/keyofDoesntContainSymbols.symbols +++ b/tests/baselines/reference/keyofDoesntContainSymbols.symbols @@ -3,70 +3,85 @@ const sym = Symbol(); >sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) >Symbol : Symbol(Symbol, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --)) -const obj = { num: 0, str: 's', [sym]: sym }; ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) ->num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 13)) ->str : Symbol(str, Decl(keyofDoesntContainSymbols.ts, 1, 21)) ->[sym] : Symbol([sym], Decl(keyofDoesntContainSymbols.ts, 1, 31)) +const num = 0; +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + +const obj = { num: 0, str: 's', [num]: num as 0, [sym]: sym }; +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 2, 13)) +>str : Symbol(str, Decl(keyofDoesntContainSymbols.ts, 2, 21)) +>[num] : Symbol([num], Decl(keyofDoesntContainSymbols.ts, 2, 31)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>[sym] : Symbol([sym], Decl(keyofDoesntContainSymbols.ts, 2, 48)) >sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) >sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) function set (obj: T, key: K, value: T[K]): T[K] { ->set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) ->K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 3, 52)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) ->key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 3, 59)) ->K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) ->value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 3, 67)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) ->K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 3, 14)) ->K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 3, 31)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 2, 62)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 4, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 4, 31)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 4, 14)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 4, 52)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 4, 14)) +>key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 4, 59)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 4, 31)) +>value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 4, 67)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 4, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 4, 31)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 4, 14)) +>K : Symbol(K, Decl(keyofDoesntContainSymbols.ts, 4, 31)) return obj[key] = value; ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 3, 52)) ->key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 3, 59)) ->value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 3, 67)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 4, 52)) +>key : Symbol(key, Decl(keyofDoesntContainSymbols.ts, 4, 59)) +>value : Symbol(value, Decl(keyofDoesntContainSymbols.ts, 4, 67)) } const val = set(obj, 'str', ''); ->val : Symbol(val, Decl(keyofDoesntContainSymbols.ts, 7, 5)) ->set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>val : Symbol(val, Decl(keyofDoesntContainSymbols.ts, 8, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 2, 62)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) // string const valB = set(obj, 'num', ''); ->valB : Symbol(valB, Decl(keyofDoesntContainSymbols.ts, 9, 5)) ->set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>valB : Symbol(valB, Decl(keyofDoesntContainSymbols.ts, 10, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 2, 62)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) // Expect type error // Argument of type '""' is not assignable to parameter of type 'number'. const valC = set(obj, sym, sym); ->valC : Symbol(valC, Decl(keyofDoesntContainSymbols.ts, 12, 5)) ->set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 1, 45)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>valC : Symbol(valC, Decl(keyofDoesntContainSymbols.ts, 13, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 2, 62)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) >sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) >sym : Symbol(sym, Decl(keyofDoesntContainSymbols.ts, 0, 5)) // Expect type error -// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +// Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" +const valD = set(obj, num, num); +>valD : Symbol(valD, Decl(keyofDoesntContainSymbols.ts, 16, 5)) +>set : Symbol(set, Decl(keyofDoesntContainSymbols.ts, 2, 62)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>num : Symbol(num, Decl(keyofDoesntContainSymbols.ts, 1, 5)) + +// Expect type error +// Argument of type '0' is not assignable to parameter of type "str" | "num" type KeyofObj = keyof typeof obj; ->KeyofObj : Symbol(KeyofObj, Decl(keyofDoesntContainSymbols.ts, 12, 32)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>KeyofObj : Symbol(KeyofObj, Decl(keyofDoesntContainSymbols.ts, 16, 32)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) // "str" | "num" type Values = T[keyof T]; ->Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 15, 33)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) ->T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 17, 12)) +>Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 19, 33)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 21, 12)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 21, 12)) +>T : Symbol(T, Decl(keyofDoesntContainSymbols.ts, 21, 12)) type ValuesOfObj = Values; ->ValuesOfObj : Symbol(ValuesOfObj, Decl(keyofDoesntContainSymbols.ts, 17, 28)) ->Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 15, 33)) ->obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 1, 5)) +>ValuesOfObj : Symbol(ValuesOfObj, Decl(keyofDoesntContainSymbols.ts, 21, 28)) +>Values : Symbol(Values, Decl(keyofDoesntContainSymbols.ts, 19, 33)) +>obj : Symbol(obj, Decl(keyofDoesntContainSymbols.ts, 2, 5)) diff --git a/tests/baselines/reference/keyofDoesntContainSymbols.types b/tests/baselines/reference/keyofDoesntContainSymbols.types index f2bd8e3568909..e07f6a234c13e 100644 --- a/tests/baselines/reference/keyofDoesntContainSymbols.types +++ b/tests/baselines/reference/keyofDoesntContainSymbols.types @@ -4,13 +4,21 @@ const sym = Symbol(); >Symbol() : unique symbol >Symbol : SymbolConstructor -const obj = { num: 0, str: 's', [sym]: sym }; ->obj : { num: number; str: string; [sym]: symbol; } ->{ num: 0, str: 's', [sym]: sym } : { num: number; str: string; [sym]: symbol; } +const num = 0; +>num : 0 +>0 : 0 + +const obj = { num: 0, str: 's', [num]: num as 0, [sym]: sym }; +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } +>{ num: 0, str: 's', [num]: num as 0, [sym]: sym } : { num: number; str: string; [num]: 0; [sym]: symbol; } >num : number >0 : 0 >str : string >'s' : "s" +>[num] : 0 +>num : 0 +>num as 0 : 0 +>num : 0 >[sym] : symbol >sym : unique symbol >sym : unique symbol @@ -42,7 +50,7 @@ const val = set(obj, 'str', ''); >val : string >set(obj, 'str', '') : string >set : (obj: T, key: K, value: T[K]) => T[K] ->obj : { num: number; str: string; [sym]: symbol; } +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } >'str' : "str" >'' : "" @@ -51,7 +59,7 @@ const valB = set(obj, 'num', ''); >valB : any >set(obj, 'num', '') : any >set : (obj: T, key: K, value: T[K]) => T[K] ->obj : { num: number; str: string; [sym]: symbol; } +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } >'num' : "num" >'' : "" @@ -61,15 +69,25 @@ const valC = set(obj, sym, sym); >valC : any >set(obj, sym, sym) : any >set : (obj: T, key: K, value: T[K]) => T[K] ->obj : { num: number; str: string; [sym]: symbol; } +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } >sym : unique symbol >sym : unique symbol // Expect type error -// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +// Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" +const valD = set(obj, num, num); +>valD : any +>set(obj, num, num) : any +>set : (obj: T, key: K, value: T[K]) => T[K] +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } +>num : 0 +>num : 0 + +// Expect type error +// Argument of type '0' is not assignable to parameter of type "str" | "num" type KeyofObj = keyof typeof obj; >KeyofObj : "str" | "num" ->obj : { num: number; str: string; [sym]: symbol; } +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } // "str" | "num" type Values = T[keyof T]; @@ -81,5 +99,5 @@ type Values = T[keyof T]; type ValuesOfObj = Values; >ValuesOfObj : string | number >Values : T[keyof T] ->obj : { num: number; str: string; [sym]: symbol; } +>obj : { num: number; str: string; [num]: 0; [sym]: symbol; } diff --git a/tests/cases/compiler/keyofDoesntContainSymbols.ts b/tests/cases/compiler/keyofDoesntContainSymbols.ts index 991a7a97e4eab..5690d4c6b8825 100644 --- a/tests/cases/compiler/keyofDoesntContainSymbols.ts +++ b/tests/cases/compiler/keyofDoesntContainSymbols.ts @@ -1,6 +1,7 @@ // @lib: es6 const sym = Symbol(); -const obj = { num: 0, str: 's', [sym]: sym }; +const num = 0; +const obj = { num: 0, str: 's', [num]: num as 0, [sym]: sym }; function set (obj: T, key: K, value: T[K]): T[K] { return obj[key] = value; @@ -13,7 +14,10 @@ const valB = set(obj, 'num', ''); // Argument of type '""' is not assignable to parameter of type 'number'. const valC = set(obj, sym, sym); // Expect type error -// Argument of type 'unique symbol' is not assignable to parameter of type '"str" | "num" +// Argument of type 'unique symbol' is not assignable to parameter of type "str" | "num" +const valD = set(obj, num, num); +// Expect type error +// Argument of type '0' is not assignable to parameter of type "str" | "num" type KeyofObj = keyof typeof obj; // "str" | "num" type Values = T[keyof T]; From 838228376fe3d0f3e0ae09a984baa1fa3f99a2f6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 4 Apr 2018 16:54:11 -0700 Subject: [PATCH 3/3] Inline ternary --- src/compiler/checker.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8222ff0f205df..908e2ce665d95 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8142,12 +8142,7 @@ namespace ts { function getLiteralTypeFromPropertyNames(type: Type, includeDeclaredTypes?: boolean) { const originalKeys = map(getPropertiesOfType(type), getLiteralTypeFromPropertyName); - if (includeDeclaredTypes) { - return getUnionType(originalKeys); - } - else { - return getUnionType(filter(originalKeys, isTypeString)); - } + return getUnionType(includeDeclaredTypes ? originalKeys : filter(originalKeys, isTypeString)); } function getIndexType(type: Type, includeDeclaredTypes?: boolean): Type {