support generic type when checking implicit conversion of symbol to string (#44578)

Co-authored-by: Wesley Wigham <wewigham@microsoft.com>
This commit is contained in:
Zzzen
2022-03-04 15:57:34 -08:00
committed by GitHub
co-authored by Wesley Wigham
parent 774899f8b9
commit f9ae305dda
6 changed files with 338 additions and 5 deletions
+13 -4
View File
@@ -32810,7 +32810,7 @@ namespace ts {
case SyntaxKind.MinusToken:
case SyntaxKind.TildeToken:
checkNonNullType(operandType, node.operand);
if (maybeTypeOfKind(operandType, TypeFlags.ESSymbolLike)) {
if (maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlags.ESSymbolLike)) {
error(node.operand, Diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, tokenToString(node.operator));
}
if (node.operator === SyntaxKind.PlusToken) {
@@ -32871,6 +32871,15 @@ namespace ts {
return numberType;
}
function maybeTypeOfKindConsideringBaseConstraint(type: Type, kind: TypeFlags): boolean {
if (maybeTypeOfKind(type, kind)) {
return true;
}
const baseConstraint = getBaseConstraintOrType(type);
return !!baseConstraint && maybeTypeOfKind(baseConstraint, kind);
}
// Return true if type might be of the given kind. A union or intersection type might be of a given
// kind if at least one constituent type is of the given kind.
function maybeTypeOfKind(type: Type, kind: TypeFlags): boolean {
@@ -33654,8 +33663,8 @@ namespace ts {
// Return true if there was no error, false if there was an error.
function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean {
const offendingSymbolOperand =
maybeTypeOfKind(leftType, TypeFlags.ESSymbolLike) ? left :
maybeTypeOfKind(rightType, TypeFlags.ESSymbolLike) ? right :
maybeTypeOfKindConsideringBaseConstraint(leftType, TypeFlags.ESSymbolLike) ? left :
maybeTypeOfKindConsideringBaseConstraint(rightType, TypeFlags.ESSymbolLike) ? right :
undefined;
if (offendingSymbolOperand) {
@@ -33893,7 +33902,7 @@ namespace ts {
const types = [];
for (const span of node.templateSpans) {
const type = checkExpression(span.expression);
if (maybeTypeOfKind(type, TypeFlags.ESSymbolLike)) {
if (maybeTypeOfKindConsideringBaseConstraint(type, TypeFlags.ESSymbolLike)) {
error(span.expression, Diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String);
}
texts.push(span.literal.text);
@@ -3,9 +3,19 @@ tests/cases/compiler/noImplicitSymbolToString.ts(7,30): error TS2469: The '+' op
tests/cases/compiler/noImplicitSymbolToString.ts(8,8): error TS2469: The '+=' operator cannot be applied to type 'symbol'.
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(...)'.
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(...)'.
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(...)'.
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(...)'.
tests/cases/compiler/noImplicitSymbolToString.ts(27,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
tests/cases/compiler/noImplicitSymbolToString.ts(28,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
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(...)'.
tests/cases/compiler/noImplicitSymbolToString.ts(32,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
tests/cases/compiler/noImplicitSymbolToString.ts(33,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
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(...)'.
tests/cases/compiler/noImplicitSymbolToString.ts(44,5): error TS2469: The '+' operator cannot be applied to type 'symbol'.
tests/cases/compiler/noImplicitSymbolToString.ts(45,6): error TS2469: The '+' operator cannot be applied to type 'symbol'.
==== tests/cases/compiler/noImplicitSymbolToString.ts (5 errors) ====
==== tests/cases/compiler/noImplicitSymbolToString.ts (15 errors) ====
// Fix #19666
let symbol!: symbol;
@@ -29,4 +39,57 @@ tests/cases/compiler/noImplicitSymbolToString.ts(13,90): error TS2731: Implicit
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
~~~~~~~~~~~~~~~~~
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
// Fix #44462
type StringOrSymbol = string | symbol;
function getKey<S extends StringOrSymbol>(key: S) {
return `${key} is the key`;
~~~
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
}
function getKey1<S extends symbol>(key: S) {
let s1!: S;
`${s1}`;
~~
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
s1 + '';
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
+s1;
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
let s2!: S | string;
`${s2}`;
~~
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
s2 + '';
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
+s2;
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
}
function getKey2<S extends string>(key: S) {
let s1!: S;
`${s1}`;
s1 + '';
+s1;
let s2!: S | symbol;
`${s2}`;
~~
!!! error TS2731: Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.
s2 + '';
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
+s2;
~~
!!! error TS2469: The '+' operator cannot be applied to type 'symbol'.
}
@@ -12,6 +12,39 @@ let symbolUnionNumber!: symbol | number;
let symbolUnionString!: symbol | string;
const templateStrUnion = `union with number ${symbolUnionNumber} and union with string ${symbolUnionString}`;
// Fix #44462
type StringOrSymbol = string | symbol;
function getKey<S extends StringOrSymbol>(key: S) {
return `${key} is the key`;
}
function getKey1<S extends symbol>(key: S) {
let s1!: S;
`${s1}`;
s1 + '';
+s1;
let s2!: S | string;
`${s2}`;
s2 + '';
+s2;
}
function getKey2<S extends string>(key: S) {
let s1!: S;
`${s1}`;
s1 + '';
+s1;
let s2!: S | symbol;
`${s2}`;
s2 + '';
+s2;
}
//// [noImplicitSymbolToString.js]
@@ -24,3 +57,26 @@ str += symbol;
var symbolUnionNumber;
var symbolUnionString;
var templateStrUnion = "union with number ".concat(symbolUnionNumber, " and union with string ").concat(symbolUnionString);
function getKey(key) {
return "".concat(key, " is the key");
}
function getKey1(key) {
var s1;
"".concat(s1);
s1 + '';
+s1;
var s2;
"".concat(s2);
s2 + '';
+s2;
}
function getKey2(key) {
var s1;
"".concat(s1);
s1 + '';
+s1;
var s2;
"".concat(s2);
s2 + '';
+s2;
}
@@ -30,3 +30,86 @@ const templateStrUnion = `union with number ${symbolUnionNumber} and union with
>symbolUnionNumber : Symbol(symbolUnionNumber, Decl(noImplicitSymbolToString.ts, 9, 3))
>symbolUnionString : Symbol(symbolUnionString, Decl(noImplicitSymbolToString.ts, 10, 3))
// Fix #44462
type StringOrSymbol = string | symbol;
>StringOrSymbol : Symbol(StringOrSymbol, Decl(noImplicitSymbolToString.ts, 12, 109))
function getKey<S extends StringOrSymbol>(key: S) {
>getKey : Symbol(getKey, Decl(noImplicitSymbolToString.ts, 17, 38))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 19, 16))
>StringOrSymbol : Symbol(StringOrSymbol, Decl(noImplicitSymbolToString.ts, 12, 109))
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 19, 42))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 19, 16))
return `${key} is the key`;
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 19, 42))
}
function getKey1<S extends symbol>(key: S) {
>getKey1 : Symbol(getKey1, Decl(noImplicitSymbolToString.ts, 21, 1))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 23, 35))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
let s1!: S;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
`${s1}`;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
s1 + '';
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
+s1;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 24, 7))
let s2!: S | string;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 23, 17))
`${s2}`;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
s2 + '';
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
+s2;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 29, 7))
}
function getKey2<S extends string>(key: S) {
>getKey2 : Symbol(getKey2, Decl(noImplicitSymbolToString.ts, 33, 1))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
>key : Symbol(key, Decl(noImplicitSymbolToString.ts, 35, 35))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
let s1!: S;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
`${s1}`;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
s1 + '';
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
+s1;
>s1 : Symbol(s1, Decl(noImplicitSymbolToString.ts, 36, 7))
let s2!: S | symbol;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
>S : Symbol(S, Decl(noImplicitSymbolToString.ts, 35, 17))
`${s2}`;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
s2 + '';
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
+s2;
>s2 : Symbol(s2, Decl(noImplicitSymbolToString.ts, 41, 7))
}
@@ -36,3 +36,92 @@ const templateStrUnion = `union with number ${symbolUnionNumber} and union with
>symbolUnionNumber : number | symbol
>symbolUnionString : string | symbol
// Fix #44462
type StringOrSymbol = string | symbol;
>StringOrSymbol : StringOrSymbol
function getKey<S extends StringOrSymbol>(key: S) {
>getKey : <S extends StringOrSymbol>(key: S) => string
>key : S
return `${key} is the key`;
>`${key} is the key` : string
>key : S
}
function getKey1<S extends symbol>(key: S) {
>getKey1 : <S extends symbol>(key: S) => void
>key : S
let s1!: S;
>s1 : S
`${s1}`;
>`${s1}` : string
>s1 : S
s1 + '';
>s1 + '' : string
>s1 : S
>'' : ""
+s1;
>+s1 : number
>s1 : S
let s2!: S | string;
>s2 : string | S
`${s2}`;
>`${s2}` : string
>s2 : string | S
s2 + '';
>s2 + '' : string
>s2 : string | S
>'' : ""
+s2;
>+s2 : number
>s2 : string | S
}
function getKey2<S extends string>(key: S) {
>getKey2 : <S extends string>(key: S) => void
>key : S
let s1!: S;
>s1 : S
`${s1}`;
>`${s1}` : string
>s1 : S
s1 + '';
>s1 + '' : string
>s1 : S
>'' : ""
+s1;
>+s1 : number
>s1 : S
let s2!: S | symbol;
>s2 : symbol | S
`${s2}`;
>`${s2}` : string
>s2 : symbol | S
s2 + '';
>s2 + '' : string
>s2 : symbol | S
>'' : ""
+s2;
>+s2 : number
>s2 : symbol | S
}
@@ -11,3 +11,36 @@ let symbolUnionNumber!: symbol | number;
let symbolUnionString!: symbol | string;
const templateStrUnion = `union with number ${symbolUnionNumber} and union with string ${symbolUnionString}`;
// Fix #44462
type StringOrSymbol = string | symbol;
function getKey<S extends StringOrSymbol>(key: S) {
return `${key} is the key`;
}
function getKey1<S extends symbol>(key: S) {
let s1!: S;
`${s1}`;
s1 + '';
+s1;
let s2!: S | string;
`${s2}`;
s2 + '';
+s2;
}
function getKey2<S extends string>(key: S) {
let s1!: S;
`${s1}`;
s1 + '';
+s1;
let s2!: S | symbol;
`${s2}`;
s2 + '';
+s2;
}