Skip to content

Commit f9ae305

Browse files
Zzzenweswigham
andauthored
support generic type when checking implicit conversion of symbol to string (#44578)
Co-authored-by: Wesley Wigham <wewigham@microsoft.com>
1 parent 774899f commit f9ae305

File tree

6 files changed

+338
-5
lines changed

6 files changed

+338
-5
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32810,7 +32810,7 @@ namespace ts {
3281032810
case SyntaxKind.MinusToken:
3281132811
case SyntaxKind.TildeToken:
3281232812
checkNonNullType(operandType, node.operand);
32813-
if (maybeTypeOfKind(operandType, TypeFlags.ESSymbolLike)) {
32813+
if (maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlags.ESSymbolLike)) {
3281432814
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator));
3281532815
}
3281632816
if (node.operator === SyntaxKind.PlusToken) {
@@ -32871,6 +32871,15 @@ namespace ts {
3287132871
return numberType;
3287232872
}
3287332873

32874+
function maybeTypeOfKindConsideringBaseConstraint(type: Type, kind: TypeFlags): boolean {
32875+
if (maybeTypeOfKind(type, kind)) {
32876+
return true;
32877+
}
32878+
32879+
const baseConstraint = getBaseConstraintOrType(type);
32880+
return !!baseConstraint && maybeTypeOfKind(baseConstraint, kind);
32881+
}
32882+
3287432883
// Return true if type might be of the given kind. A union or intersection type might be of a given
3287532884
// kind if at least one constituent type is of the given kind.
3287632885
function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean {
@@ -33654,8 +33663,8 @@ namespace ts {
3365433663
// Return true if there was no error, false if there was an error.
3365533664
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
3365633665
const offendingSymbolOperand =
33657-
maybeTypeOfKind(leftType, TypeFlags.ESSymbolLike) ? left :
33658-
maybeTypeOfKind(rightType, TypeFlags.ESSymbolLike) ? right :
33666+
maybeTypeOfKindConsideringBaseConstraint(leftType, TypeFlags.ESSymbolLike) ? left :
33667+
maybeTypeOfKindConsideringBaseConstraint(rightType, TypeFlags.ESSymbolLike) ? right :
3365933668
undefined;
3366033669

3366133670
if (offendingSymbolOperand) {
@@ -33893,7 +33902,7 @@ namespace ts {
3389333902
const types = [];
3389433903
for (const span of node.templateSpans) {
3389533904
const type = checkExpression(span.expression);
33896-
if (maybeTypeOfKind(type, TypeFlags.ESSymbolLike)) {
33905+
if (maybeTypeOfKindConsideringBaseConstraint(type, TypeFlags.ESSymbolLike)) {
3389733906
error(span.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String);
3389833907
}
3389933908
texts.push(span.literal.text);

tests/baselines/reference/noImplicitSymbolToString.errors.txt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@ tests/cases/compiler/noImplicitSymbolToString.ts(7,30): error TS2469: The '+' op
33
tests/cases/compiler/noImplicitSymbolToString.ts(8,8): error TS2469: The '+=' operator cannot be applied to type 'symbol'.
44
tests/cases/compiler/noImplicitSymbolToString.ts(13,47): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
55
tests/cases/compiler/noImplicitSymbolToString.ts(13,90): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
6+
tests/cases/compiler/noImplicitSymbolToString.ts(21,15): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
7+
tests/cases/compiler/noImplicitSymbolToString.ts(26,8): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
8+
tests/cases/compiler/noImplicitSymbolToString.ts(27,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
9+
tests/cases/compiler/noImplicitSymbolToString.ts(28,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
10+
tests/cases/compiler/noImplicitSymbolToString.ts(31,8): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
11+
tests/cases/compiler/noImplicitSymbolToString.ts(32,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
12+
tests/cases/compiler/noImplicitSymbolToString.ts(33,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
13+
tests/cases/compiler/noImplicitSymbolToString.ts(43,8): error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
14+
tests/cases/compiler/noImplicitSymbolToString.ts(44,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
15+
tests/cases/compiler/noImplicitSymbolToString.ts(45,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
616

717

8-
==== tests/cases/compiler/noImplicitSymbolToString.ts (5 errors) ====
18+
==== tests/cases/compiler/noImplicitSymbolToString.ts (15 errors) ====
919
// Fix #19666
1020

1121
let symbol!: symbol;
@@ -29,4 +39,57 @@ tests/cases/compiler/noImplicitSymbolToString.ts(13,90): error TS2731: Implicit
2939
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
3040
~~~~~~~~~~~~~~~~~
3141
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
42+
43+
44+
// Fix #44462
45+
46+
type StringOrSymbol = string | symbol;
47+
48+
function getKey<S extends StringOrSymbol>(key: S) {
49+
return `${key} is the key`;
50+
~~~
51+
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
52+
}
53+
54+
function getKey1<S extends symbol>(key: S) {
55+
let s1!: S;
56+
`${s1}`;
57+
~~
58+
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
59+
s1 + '';
60+
~~
61+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
62+
+s1;
63+
~~
64+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
65+
66+
let s2!: S | string;
67+
`${s2}`;
68+
~~
69+
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
70+
s2 + '';
71+
~~
72+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
73+
+s2;
74+
~~
75+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
76+
}
77+
78+
function getKey2<S extends string>(key: S) {
79+
let s1!: S;
80+
`${s1}`;
81+
s1 + '';
82+
+s1;
83+
84+
let s2!: S | symbol;
85+
`${s2}`;
86+
~~
87+
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
88+
s2 + '';
89+
~~
90+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
91+
+s2;
92+
~~
93+
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
94+
}
3295

tests/baselines/reference/noImplicitSymbolToString.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,39 @@ let symbolUnionNumber!: symbol | number;
1212
let symbolUnionString!: symbol | string;
1313

1414
const templateStrUnion = `union with number ${symbolUnionNumber} and union with string ${symbolUnionString}`;
15+
16+
17+
// Fix #44462
18+
19+
type StringOrSymbol = string | symbol;
20+
21+
function getKey<S extends StringOrSymbol>(key: S) {
22+
return `${key} is the key`;
23+
}
24+
25+
function getKey1<S extends symbol>(key: S) {
26+
let s1!: S;
27+
`${s1}`;
28+
s1 + '';
29+
+s1;
30+
31+
let s2!: S | string;
32+
`${s2}`;
33+
s2 + '';
34+
+s2;
35+
}
36+
37+
function getKey2<S extends string>(key: S) {
38+
let s1!: S;
39+
`${s1}`;
40+
s1 + '';
41+
+s1;
42+
43+
let s2!: S | symbol;
44+
`${s2}`;
45+
s2 + '';
46+
+s2;
47+
}
1548

1649

1750
//// [noImplicitSymbolToString.js]
@@ -24,3 +57,26 @@ str += symbol;
2457
var symbolUnionNumber;
2558
var symbolUnionString;
2659
var templateStrUnion = "union with number ".concat(symbolUnionNumber, " and union with string ").concat(symbolUnionString);
60+
function getKey(key) {
61+
return "".concat(key, " is the key");
62+
}
63+
function getKey1(key) {
64+
var s1;
65+
"".concat(s1);
66+
s1 + '';
67+
+s1;
68+
var s2;
69+
"".concat(s2);
70+
s2 + '';
71+
+s2;
72+
}
73+
function getKey2(key) {
74+
var s1;
75+
"".concat(s1);
76+
s1 + '';
77+
+s1;
78+
var s2;
79+
"".concat(s2);
80+
s2 + '';
81+
+s2;
82+
}

tests/baselines/reference/noImplicitSymbolToString.symbols

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,86 @@ const templateStrUnion = `union with number ${symbolUnionNumber} and union with
3030
>symbolUnionNumber : Symbol(symbolUnionNumber, Decl(noImplicitSymbolToString.ts, 9, 3))
3131
>symbolUnionString : Symbol(symbolUnionString, Decl(noImplicitSymbolToString.ts, 10, 3))
3232

33+
34+
// Fix #44462
35+
36+
type StringOrSymbol = string | symbol;
37+
>StringOrSymbol : Symbol(StringOrSymbol, Decl(noImplicitSymbolToString.ts, 12, 109))
38+
39+
function getKey<S extends StringOrSymbol>(key: S) {
40+
>getKey : Symbol(getKey, Decl(noImplicitSymbolToString.ts, 17, 38))
41+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 19, 16))
42+
>StringOrSymbol : Symbol(StringOrSymbol, Decl(noImplicitSymbolToString.ts, 12, 109))
43+
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 19, 42))
44+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 19, 16))
45+
46+
return `${key} is the key`;
47+
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 19, 42))
48+
}
49+
50+
function getKey1<S extends symbol>(key: S) {
51+
>getKey1 : Symbol(getKey1, Decl(noImplicitSymbolToString.ts, 21, 1))
52+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
53+
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 23, 35))
54+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
55+
56+
let s1!: S;
57+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
58+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
59+
60+
`${s1}`;
61+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
62+
63+
s1 + '';
64+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
65+
66+
+s1;
67+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
68+
69+
let s2!: S | string;
70+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
71+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
72+
73+
`${s2}`;
74+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
75+
76+
s2 + '';
77+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
78+
79+
+s2;
80+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
81+
}
82+
83+
function getKey2<S extends string>(key: S) {
84+
>getKey2 : Symbol(getKey2, Decl(noImplicitSymbolToString.ts, 33, 1))
85+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
86+
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 35, 35))
87+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
88+
89+
let s1!: S;
90+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
91+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
92+
93+
`${s1}`;
94+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
95+
96+
s1 + '';
97+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
98+
99+
+s1;
100+
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
101+
102+
let s2!: S | symbol;
103+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
104+
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
105+
106+
`${s2}`;
107+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
108+
109+
s2 + '';
110+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
111+
112+
+s2;
113+
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
114+
}
115+

tests/baselines/reference/noImplicitSymbolToString.types

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,92 @@ const templateStrUnion = `union with number ${symbolUnionNumber} and union with
3636
>symbolUnionNumber : number | symbol
3737
>symbolUnionString : string | symbol
3838

39+
40+
// Fix #44462
41+
42+
type StringOrSymbol = string | symbol;
43+
>StringOrSymbol : StringOrSymbol
44+
45+
function getKey<S extends StringOrSymbol>(key: S) {
46+
>getKey : <S extends StringOrSymbol>(key: S) => string
47+
>key : S
48+
49+
return `${key} is the key`;
50+
>`${key} is the key` : string
51+
>key : S
52+
}
53+
54+
function getKey1<S extends symbol>(key: S) {
55+
>getKey1 : <S extends symbol>(key: S) => void
56+
>key : S
57+
58+
let s1!: S;
59+
>s1 : S
60+
61+
`${s1}`;
62+
>`${s1}` : string
63+
>s1 : S
64+
65+
s1 + '';
66+
>s1 + '' : string
67+
>s1 : S
68+
>'' : ""
69+
70+
+s1;
71+
>+s1 : number
72+
>s1 : S
73+
74+
let s2!: S | string;
75+
>s2 : string | S
76+
77+
`${s2}`;
78+
>`${s2}` : string
79+
>s2 : string | S
80+
81+
s2 + '';
82+
>s2 + '' : string
83+
>s2 : string | S
84+
>'' : ""
85+
86+
+s2;
87+
>+s2 : number
88+
>s2 : string | S
89+
}
90+
91+
function getKey2<S extends string>(key: S) {
92+
>getKey2 : <S extends string>(key: S) => void
93+
>key : S
94+
95+
let s1!: S;
96+
>s1 : S
97+
98+
`${s1}`;
99+
>`${s1}` : string
100+
>s1 : S
101+
102+
s1 + '';
103+
>s1 + '' : string
104+
>s1 : S
105+
>'' : ""
106+
107+
+s1;
108+
>+s1 : number
109+
>s1 : S
110+
111+
let s2!: S | symbol;
112+
>s2 : symbol | S
113+
114+
`${s2}`;
115+
>`${s2}` : string
116+
>s2 : symbol | S
117+
118+
s2 + '';
119+
>s2 + '' : string
120+
>s2 : symbol | S
121+
>'' : ""
122+
123+
+s2;
124+
>+s2 : number
125+
>s2 : symbol | S
126+
}
127+

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy