Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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