Skip to content

Commit dbba8b3

Browse files
authored
Make optional properties assignable to string index signatures (#41921)
1 parent 35c8df0 commit dbba8b3

6 files changed

+222
-1
lines changed

src/compiler/checker.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -18609,7 +18609,11 @@ namespace ts {
1860918609
continue;
1861018610
}
1861118611
if (kind === IndexKind.String || isNumericLiteralName(prop.escapedName)) {
18612-
const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors);
18612+
const propType = getTypeOfSymbol(prop);
18613+
const type = propType.flags & TypeFlags.Undefined || !(kind === IndexKind.String && prop.flags & SymbolFlags.Optional)
18614+
? propType
18615+
: getTypeWithFacts(propType, TypeFacts.NEUndefined);
18616+
const related = isRelatedTo(type, target, reportErrors);
1861318617
if (!related) {
1861418618
if (reportErrors) {
1861518619
reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(6,1): error TS2322: Type '{ k1: string | undefined; }' is not assignable to type '{ [key: string]: string; }'.
2+
Property 'k1' is incompatible with index signature.
3+
Type 'string | undefined' is not assignable to type 'string'.
4+
Type 'undefined' is not assignable to type 'string'.
5+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(10,1): error TS2322: Type '{ 1?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'.
6+
Property '1' is incompatible with index signature.
7+
Type 'string | undefined' is not assignable to type 'string'.
8+
Type 'undefined' is not assignable to type 'string'.
9+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts(13,5): error TS2322: Type '{ k1?: undefined; }' is not assignable to type '{ [key: string]: string; }'.
10+
Property 'k1' is incompatible with index signature.
11+
Type 'undefined' is not assignable to type 'string'.
12+
13+
14+
==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts (3 errors) ====
15+
declare let optionalProperties: { k1?: string };
16+
declare let undefinedProperties: { k1: string | undefined };
17+
18+
declare let stringDictionary: { [key: string]: string };
19+
stringDictionary = optionalProperties; // ok
20+
stringDictionary = undefinedProperties; // error
21+
~~~~~~~~~~~~~~~~
22+
!!! error TS2322: Type '{ k1: string | undefined; }' is not assignable to type '{ [key: string]: string; }'.
23+
!!! error TS2322: Property 'k1' is incompatible with index signature.
24+
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'.
25+
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
26+
27+
declare let probablyArray: { [key: number]: string };
28+
declare let numberLiteralKeys: { 1?: string };
29+
probablyArray = numberLiteralKeys; // error
30+
~~~~~~~~~~~~~
31+
!!! error TS2322: Type '{ 1?: string | undefined; }' is not assignable to type '{ [key: number]: string; }'.
32+
!!! error TS2322: Property '1' is incompatible with index signature.
33+
!!! error TS2322: Type 'string | undefined' is not assignable to type 'string'.
34+
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
35+
36+
declare let optionalUndefined: { k1?: undefined };
37+
let dict: { [key: string]: string } = optionalUndefined; // error
38+
~~~~
39+
!!! error TS2322: Type '{ k1?: undefined; }' is not assignable to type '{ [key: string]: string; }'.
40+
!!! error TS2322: Property 'k1' is incompatible with index signature.
41+
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
42+
43+
function f<T>() {
44+
let optional: { k1?: T } = undefined!;
45+
let dict: { [key: string]: T | number } = optional; // ok
46+
}
47+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//// [optionalPropertyAssignableToStringIndexSignature.ts]
2+
declare let optionalProperties: { k1?: string };
3+
declare let undefinedProperties: { k1: string | undefined };
4+
5+
declare let stringDictionary: { [key: string]: string };
6+
stringDictionary = optionalProperties; // ok
7+
stringDictionary = undefinedProperties; // error
8+
9+
declare let probablyArray: { [key: number]: string };
10+
declare let numberLiteralKeys: { 1?: string };
11+
probablyArray = numberLiteralKeys; // error
12+
13+
declare let optionalUndefined: { k1?: undefined };
14+
let dict: { [key: string]: string } = optionalUndefined; // error
15+
16+
function f<T>() {
17+
let optional: { k1?: T } = undefined!;
18+
let dict: { [key: string]: T | number } = optional; // ok
19+
}
20+
21+
22+
//// [optionalPropertyAssignableToStringIndexSignature.js]
23+
"use strict";
24+
stringDictionary = optionalProperties; // ok
25+
stringDictionary = undefinedProperties; // error
26+
probablyArray = numberLiteralKeys; // error
27+
var dict = optionalUndefined; // error
28+
function f() {
29+
var optional = undefined;
30+
var dict = optional; // ok
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts ===
2+
declare let optionalProperties: { k1?: string };
3+
>optionalProperties : Symbol(optionalProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 11))
4+
>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 33))
5+
6+
declare let undefinedProperties: { k1: string | undefined };
7+
>undefinedProperties : Symbol(undefinedProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 11))
8+
>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 34))
9+
10+
declare let stringDictionary: { [key: string]: string };
11+
>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11))
12+
>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 33))
13+
14+
stringDictionary = optionalProperties; // ok
15+
>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11))
16+
>optionalProperties : Symbol(optionalProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 0, 11))
17+
18+
stringDictionary = undefinedProperties; // error
19+
>stringDictionary : Symbol(stringDictionary, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 3, 11))
20+
>undefinedProperties : Symbol(undefinedProperties, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 1, 11))
21+
22+
declare let probablyArray: { [key: number]: string };
23+
>probablyArray : Symbol(probablyArray, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 11))
24+
>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 30))
25+
26+
declare let numberLiteralKeys: { 1?: string };
27+
>numberLiteralKeys : Symbol(numberLiteralKeys, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 11))
28+
>1 : Symbol(1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 32))
29+
30+
probablyArray = numberLiteralKeys; // error
31+
>probablyArray : Symbol(probablyArray, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 7, 11))
32+
>numberLiteralKeys : Symbol(numberLiteralKeys, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 8, 11))
33+
34+
declare let optionalUndefined: { k1?: undefined };
35+
>optionalUndefined : Symbol(optionalUndefined, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 11))
36+
>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 32))
37+
38+
let dict: { [key: string]: string } = optionalUndefined; // error
39+
>dict : Symbol(dict, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 3))
40+
>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 13))
41+
>optionalUndefined : Symbol(optionalUndefined, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 11, 11))
42+
43+
function f<T>() {
44+
>f : Symbol(f, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 12, 56))
45+
>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11))
46+
47+
let optional: { k1?: T } = undefined!;
48+
>optional : Symbol(optional, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 4))
49+
>k1 : Symbol(k1, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 16))
50+
>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11))
51+
>undefined : Symbol(undefined)
52+
53+
let dict: { [key: string]: T | number } = optional; // ok
54+
>dict : Symbol(dict, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 16, 4))
55+
>key : Symbol(key, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 16, 14))
56+
>T : Symbol(T, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 14, 11))
57+
>optional : Symbol(optional, Decl(optionalPropertyAssignableToStringIndexSignature.ts, 15, 4))
58+
}
59+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
=== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/optionalPropertyAssignableToStringIndexSignature.ts ===
2+
declare let optionalProperties: { k1?: string };
3+
>optionalProperties : { k1?: string | undefined; }
4+
>k1 : string | undefined
5+
6+
declare let undefinedProperties: { k1: string | undefined };
7+
>undefinedProperties : { k1: string | undefined; }
8+
>k1 : string | undefined
9+
10+
declare let stringDictionary: { [key: string]: string };
11+
>stringDictionary : { [key: string]: string; }
12+
>key : string
13+
14+
stringDictionary = optionalProperties; // ok
15+
>stringDictionary = optionalProperties : { k1?: string | undefined; }
16+
>stringDictionary : { [key: string]: string; }
17+
>optionalProperties : { k1?: string | undefined; }
18+
19+
stringDictionary = undefinedProperties; // error
20+
>stringDictionary = undefinedProperties : { k1: string | undefined; }
21+
>stringDictionary : { [key: string]: string; }
22+
>undefinedProperties : { k1: string | undefined; }
23+
24+
declare let probablyArray: { [key: number]: string };
25+
>probablyArray : { [key: number]: string; }
26+
>key : number
27+
28+
declare let numberLiteralKeys: { 1?: string };
29+
>numberLiteralKeys : { 1?: string | undefined; }
30+
>1 : string | undefined
31+
32+
probablyArray = numberLiteralKeys; // error
33+
>probablyArray = numberLiteralKeys : { 1?: string | undefined; }
34+
>probablyArray : { [key: number]: string; }
35+
>numberLiteralKeys : { 1?: string | undefined; }
36+
37+
declare let optionalUndefined: { k1?: undefined };
38+
>optionalUndefined : { k1?: undefined; }
39+
>k1 : undefined
40+
41+
let dict: { [key: string]: string } = optionalUndefined; // error
42+
>dict : { [key: string]: string; }
43+
>key : string
44+
>optionalUndefined : { k1?: undefined; }
45+
46+
function f<T>() {
47+
>f : <T>() => void
48+
49+
let optional: { k1?: T } = undefined!;
50+
>optional : { k1?: T | undefined; }
51+
>k1 : T | undefined
52+
>undefined! : never
53+
>undefined : undefined
54+
55+
let dict: { [key: string]: T | number } = optional; // ok
56+
>dict : { [key: string]: number | T; }
57+
>key : string
58+
>optional : { k1?: T | undefined; }
59+
}
60+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @strict: true
2+
3+
declare let optionalProperties: { k1?: string };
4+
declare let undefinedProperties: { k1: string | undefined };
5+
6+
declare let stringDictionary: { [key: string]: string };
7+
stringDictionary = optionalProperties; // ok
8+
stringDictionary = undefinedProperties; // error
9+
10+
declare let probablyArray: { [key: number]: string };
11+
declare let numberLiteralKeys: { 1?: string };
12+
probablyArray = numberLiteralKeys; // error
13+
14+
declare let optionalUndefined: { k1?: undefined };
15+
let dict: { [key: string]: string } = optionalUndefined; // error
16+
17+
function f<T>() {
18+
let optional: { k1?: T } = undefined!;
19+
let dict: { [key: string]: T | number } = optional; // ok
20+
}

0 commit comments

Comments
 (0)