Skip to content

Commit

Permalink
support generic type when checking implicit conversion of symbol to s…
Browse files Browse the repository at this point in the history
…tring (#44578)

Co-authored-by: Wesley Wigham <[email protected]>
  • Loading branch information
Zzzen and weswigham committed Mar 4, 2022
1 parent 774899f commit f9ae305
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 5 deletions.
17 changes: 13 additions & 4 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
65 changes: 64 additions & 1 deletion tests/baselines/reference/noImplicitSymbolToString.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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'.
}

56 changes: 56 additions & 0 deletions tests/baselines/reference/noImplicitSymbolToString.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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;
}
83 changes: 83 additions & 0 deletions tests/baselines/reference/noImplicitSymbolToString.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}

89 changes: 89 additions & 0 deletions tests/baselines/reference/noImplicitSymbolToString.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Loading

0 comments on commit f9ae305

Please sign in to comment.