From c88e7f8df16fc90cddedb0df47a6e37e98a94d52 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 10 Dec 2020 11:53:08 -0800 Subject: [PATCH] Make optional properties assignable to string index signatures --- src/compiler/checker.ts | 6 +- ...ssignableToStringIndexSignature.errors.txt | 47 +++++++++++++++ ...ropertyAssignableToStringIndexSignature.js | 31 ++++++++++ ...tyAssignableToStringIndexSignature.symbols | 59 ++++++++++++++++++ ...ertyAssignableToStringIndexSignature.types | 60 +++++++++++++++++++ ...ropertyAssignableToStringIndexSignature.ts | 20 +++++++ 6 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.errors.txt create mode 100644 tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.js create mode 100644 tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.symbols create mode 100644 tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.types create mode 100644 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 08030f8452e23..41c99ddb0acef 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18478,7 +18478,11 @@ namespace ts { continue; } if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) { - const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); + const propType = getTypeOfSymbol(prop); + const type = propType.flags & TypeFlags.Undefined || !(kind === IndexKind.String && prop.flags & SymbolFlags.Optional) + ? propType + : getTypeWithFacts(propType, TypeFacts.NEUndefined); + const related = isRelatedTo(type, target, reportErrors); if (!related) { if (reportErrors) { reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); diff --git a/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.errors.txt b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.errors.txt new file mode 100644 index 0000000000000..f6ba165804206 --- /dev/null +++ b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.errors.txt @@ -0,0 +1,47 @@ +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(6,1): error TS2322: Type '{ k1: string | undefined; }' is not assignable to type '{ [key: string]: string; }'. + Property 'k1' is incompatible with index signature. + Type 'string | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(10,1): error TS2322: Type '{ 1?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'. + Property '1' is incompatible with index signature. + Type 'string | undefined' is not assignable to type 'string'. + Type 'undefined' is not assignable to type 'string'. +tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(13,5): error TS2322: Type '{ k1?: undefined; }' is not assignable to type '{ [key: string]: string; }'. + Property 'k1' is incompatible with index signature. + Type 'undefined' is not assignable to type 'string'. + + +==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts (3 errors) ==== + declare let optionalProperties: { k1?: string }; + declare let undefinedProperties: { k1: string | undefined }; + + declare let stringDictionary: { [key: string]: string }; + stringDictionary = optionalProperties; // ok + stringDictionary = undefinedProperties; // error + ~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '{ k1: string | undefined; }' is not assignable to type '{ [key: string]: string; }'. +!!! error TS2322: Property 'k1' is incompatible with index signature. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + + declare let probablyArray: { [key: number]: string }; + declare let numberLiteralKeys: { 1?: string }; + probablyArray = numberLiteralKeys; // error + ~~~~~~~~~~~~~ +!!! error TS2322: Type '{ 1?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'. +!!! error TS2322: Property '1' is incompatible with index signature. +!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + + declare let optionalUndefined: { k1?: undefined }; + let dict: { [key: string]: string } = optionalUndefined; // error + ~~~~ +!!! error TS2322: Type '{ k1?: undefined; }' is not assignable to type '{ [key: string]: string; }'. +!!! error TS2322: Property 'k1' is incompatible with index signature. +!!! error TS2322: Type 'undefined' is not assignable to type 'string'. + + function f() { + let optional: { k1?: T } = undefined!; + let dict: { [key: string]: T | number } = optional; // ok + } + \ No newline at end of file diff --git a/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.js b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.js new file mode 100644 index 0000000000000..8c9d822a127e6 --- /dev/null +++ b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.js @@ -0,0 +1,31 @@ +//// [optionalPropertyAssignableToStringIndexSignature.ts] +declare let optionalProperties: { k1?: string }; +declare let undefinedProperties: { k1: string | undefined }; + +declare let stringDictionary: { [key: string]: string }; +stringDictionary = optionalProperties; // ok +stringDictionary = undefinedProperties; // error + +declare let probablyArray: { [key: number]: string }; +declare let numberLiteralKeys: { 1?: string }; +probablyArray = numberLiteralKeys; // error + +declare let optionalUndefined: { k1?: undefined }; +let dict: { [key: string]: string } = optionalUndefined; // error + +function f() { + let optional: { k1?: T } = undefined!; + let dict: { [key: string]: T | number } = optional; // ok +} + + +//// [optionalPropertyAssignableToStringIndexSignature.js] +"use strict"; +stringDictionary = optionalProperties; // ok +stringDictionary = undefinedProperties; // error +probablyArray = numberLiteralKeys; // error +var dict = optionalUndefined; // error +function f() { + var optional = undefined; + var dict = optional; // ok +} diff --git a/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.symbols b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.symbols new file mode 100644 index 0000000000000..8da68d002b384 --- /dev/null +++ b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.symbols @@ -0,0 +1,59 @@ +=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts === +declare let optionalProperties: { k1?: string }; +>optionalProperties : Symbol(optionalProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 11)) +>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 33)) + +declare let undefinedProperties: { k1: string | undefined }; +>undefinedProperties : Symbol(undefinedProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 11)) +>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 34)) + +declare let stringDictionary: { [key: string]: string }; +>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11)) +>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 33)) + +stringDictionary = optionalProperties; // ok +>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11)) +>optionalProperties : Symbol(optionalProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 11)) + +stringDictionary = undefinedProperties; // error +>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11)) +>undefinedProperties : Symbol(undefinedProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 11)) + +declare let probablyArray: { [key: number]: string }; +>probablyArray : Symbol(probablyArray, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 11)) +>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 30)) + +declare let numberLiteralKeys: { 1?: string }; +>numberLiteralKeys : Symbol(numberLiteralKeys, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 11)) +>1 : Symbol(1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 32)) + +probablyArray = numberLiteralKeys; // error +>probablyArray : Symbol(probablyArray, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 11)) +>numberLiteralKeys : Symbol(numberLiteralKeys, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 11)) + +declare let optionalUndefined: { k1?: undefined }; +>optionalUndefined : Symbol(optionalUndefined, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 11)) +>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 32)) + +let dict: { [key: string]: string } = optionalUndefined; // error +>dict : Symbol(dict, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 3)) +>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 13)) +>optionalUndefined : Symbol(optionalUndefined, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 11)) + +function f() { +>f : Symbol(f, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 56)) +>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11)) + + let optional: { k1?: T } = undefined!; +>optional : Symbol(optional, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 4)) +>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 16)) +>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11)) +>undefined : Symbol(undefined) + + let dict: { [key: string]: T | number } = optional; // ok +>dict : Symbol(dict, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 16, 4)) +>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 16, 14)) +>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11)) +>optional : Symbol(optional, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 4)) +} + diff --git a/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.types b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.types new file mode 100644 index 0000000000000..1a8859b6e781f --- /dev/null +++ b/tests/baselines/reference/optionalPropertyAssignableToStringIndexSignature.types @@ -0,0 +1,60 @@ +=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts === +declare let optionalProperties: { k1?: string }; +>optionalProperties : { k1?: string | undefined; } +>k1 : string | undefined + +declare let undefinedProperties: { k1: string | undefined }; +>undefinedProperties : { k1: string | undefined; } +>k1 : string | undefined + +declare let stringDictionary: { [key: string]: string }; +>stringDictionary : { [key: string]: string; } +>key : string + +stringDictionary = optionalProperties; // ok +>stringDictionary = optionalProperties : { k1?: string | undefined; } +>stringDictionary : { [key: string]: string; } +>optionalProperties : { k1?: string | undefined; } + +stringDictionary = undefinedProperties; // error +>stringDictionary = undefinedProperties : { k1: string | undefined; } +>stringDictionary : { [key: string]: string; } +>undefinedProperties : { k1: string | undefined; } + +declare let probablyArray: { [key: number]: string }; +>probablyArray : { [key: number]: string; } +>key : number + +declare let numberLiteralKeys: { 1?: string }; +>numberLiteralKeys : { 1?: string | undefined; } +>1 : string | undefined + +probablyArray = numberLiteralKeys; // error +>probablyArray = numberLiteralKeys : { 1?: string | undefined; } +>probablyArray : { [key: number]: string; } +>numberLiteralKeys : { 1?: string | undefined; } + +declare let optionalUndefined: { k1?: undefined }; +>optionalUndefined : { k1?: undefined; } +>k1 : undefined + +let dict: { [key: string]: string } = optionalUndefined; // error +>dict : { [key: string]: string; } +>key : string +>optionalUndefined : { k1?: undefined; } + +function f() { +>f : () => void + + let optional: { k1?: T } = undefined!; +>optional : { k1?: T | undefined; } +>k1 : T | undefined +>undefined! : never +>undefined : undefined + + let dict: { [key: string]: T | number } = optional; // ok +>dict : { [key: string]: number | T; } +>key : string +>optional : { k1?: T | undefined; } +} + diff --git a/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts new file mode 100644 index 0000000000000..ed428bf3b878e --- /dev/null +++ b/tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts @@ -0,0 +1,20 @@ +// @strict: true + +declare let optionalProperties: { k1?: string }; +declare let undefinedProperties: { k1: string | undefined }; + +declare let stringDictionary: { [key: string]: string }; +stringDictionary = optionalProperties; // ok +stringDictionary = undefinedProperties; // error + +declare let probablyArray: { [key: number]: string }; +declare let numberLiteralKeys: { 1?: string }; +probablyArray = numberLiteralKeys; // error + +declare let optionalUndefined: { k1?: undefined }; +let dict: { [key: string]: string } = optionalUndefined; // error + +function f() { + let optional: { k1?: T } = undefined!; + let dict: { [key: string]: T | number } = optional; // ok +}