mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
feat(49962): Disallow comparison against NaN (#50626)
* feat(49962): disallow comparison against NaN * change diagnostic message * use global NaN symbol for NaN equality comparisons
This commit is contained in:
parent
23746af766
commit
e002159ad1
@ -999,6 +999,7 @@ namespace ts {
|
||||
let deferredGlobalOmitSymbol: Symbol | undefined;
|
||||
let deferredGlobalAwaitedSymbol: Symbol | undefined;
|
||||
let deferredGlobalBigIntType: ObjectType | undefined;
|
||||
let deferredGlobalNaNSymbol: Symbol | undefined;
|
||||
let deferredGlobalRecordSymbol: Symbol | undefined;
|
||||
|
||||
const allPotentiallyUnusedIdentifiers = new Map<Path, PotentiallyUnusedIdentifier[]>(); // key is file name
|
||||
@ -14343,6 +14344,10 @@ namespace ts {
|
||||
return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType;
|
||||
}
|
||||
|
||||
function getGlobalNaNSymbol(): Symbol | undefined {
|
||||
return (deferredGlobalNaNSymbol ||= getGlobalValueSymbol("NaN" as __String, /*reportErrors*/ false));
|
||||
}
|
||||
|
||||
function getGlobalRecordSymbol(): Symbol | undefined {
|
||||
deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol;
|
||||
return deferredGlobalRecordSymbol === unknownSymbol ? undefined : deferredGlobalRecordSymbol;
|
||||
@ -34495,6 +34500,7 @@ namespace ts {
|
||||
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
|
||||
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
|
||||
}
|
||||
checkNaNEquality(errorNode, operator, left, right);
|
||||
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
|
||||
return booleanType;
|
||||
|
||||
@ -34727,6 +34733,29 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function checkNaNEquality(errorNode: Node | undefined, operator: SyntaxKind, left: Expression, right: Expression) {
|
||||
const isLeftNaN = isGlobalNaN(skipParentheses(left));
|
||||
const isRightNaN = isGlobalNaN(skipParentheses(right));
|
||||
if (isLeftNaN || isRightNaN) {
|
||||
const err = error(errorNode, Diagnostics.This_condition_will_always_return_0,
|
||||
tokenToString(operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.EqualsEqualsToken ? SyntaxKind.FalseKeyword : SyntaxKind.TrueKeyword));
|
||||
if (isLeftNaN && isRightNaN) return;
|
||||
const operatorString = operator === SyntaxKind.ExclamationEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken ? tokenToString(SyntaxKind.ExclamationToken) : "";
|
||||
const location = isLeftNaN ? right : left;
|
||||
const expression = skipParentheses(location);
|
||||
addRelatedInfo(err, createDiagnosticForNode(location, Diagnostics.Did_you_mean_0,
|
||||
`${operatorString}Number.isNaN(${isEntityNameExpression(expression) ? entityNameToString(expression) : "..."})`));
|
||||
}
|
||||
}
|
||||
|
||||
function isGlobalNaN(expr: Expression): boolean {
|
||||
if (isIdentifier(expr) && expr.escapedText === "NaN") {
|
||||
const globalNaNSymbol = getGlobalNaNSymbol();
|
||||
return !!globalNaNSymbol && globalNaNSymbol === getResolvedSymbol(expr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getBaseTypesIfUnrelated(leftType: Type, rightType: Type, isRelated: (left: Type, right: Type) => boolean): [Type, Type] {
|
||||
|
||||
@ -3563,6 +3563,10 @@
|
||||
"category": "Error",
|
||||
"code": 2844
|
||||
},
|
||||
"This condition will always return '{0}'.": {
|
||||
"category": "Error",
|
||||
"code": 2845
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
@ -7356,7 +7360,14 @@
|
||||
"category": "Message",
|
||||
"code": 95173
|
||||
},
|
||||
|
||||
"Use `{0}`.": {
|
||||
"category": "Message",
|
||||
"code": 95174
|
||||
},
|
||||
"Use `Number.isNaN` in all conditions.": {
|
||||
"category": "Message",
|
||||
"code": 95175
|
||||
},
|
||||
|
||||
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -94,15 +94,6 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node {
|
||||
const end = textSpanEnd(span);
|
||||
let token = getTokenAtPosition(sourceFile, span.start);
|
||||
while (token.end < end) {
|
||||
token = token.parent;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
function tryGetConstraintFromDiagnosticMessage(messageText: string | DiagnosticMessageChain) {
|
||||
const [_, constraint] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/`extends (.*)`/) || [];
|
||||
return constraint;
|
||||
|
||||
65
src/services/codefixes/fixNaNEquality.ts
Normal file
65
src/services/codefixes/fixNaNEquality.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/* @internal */
|
||||
namespace ts.codefix {
|
||||
const fixId = "fixNaNEquality";
|
||||
const errorCodes = [
|
||||
Diagnostics.This_condition_will_always_return_0.code,
|
||||
];
|
||||
|
||||
registerCodeFix({
|
||||
errorCodes,
|
||||
getCodeActions(context) {
|
||||
const { sourceFile, span, program } = context;
|
||||
const info = getInfo(program, sourceFile, span);
|
||||
if (info === undefined) return;
|
||||
|
||||
const { suggestion, expression, arg } = info;
|
||||
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, arg, expression));
|
||||
return [createCodeFixAction(fixId, changes, [Diagnostics.Use_0, suggestion], fixId, Diagnostics.Use_Number_isNaN_in_all_conditions)];
|
||||
},
|
||||
fixIds: [fixId],
|
||||
getAllCodeActions: context => {
|
||||
return codeFixAll(context, errorCodes, (changes, diag) => {
|
||||
const info = getInfo(context.program, diag.file, createTextSpan(diag.start, diag.length));
|
||||
if (info) {
|
||||
doChange(changes, diag.file, info.arg, info.expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
interface Info {
|
||||
suggestion: string;
|
||||
expression: BinaryExpression;
|
||||
arg: Expression;
|
||||
}
|
||||
|
||||
function getInfo(program: Program, sourceFile: SourceFile, span: TextSpan): Info | undefined {
|
||||
const diag = find(program.getSemanticDiagnostics(sourceFile), diag => diag.start === span.start && diag.length === span.length);
|
||||
if (diag === undefined || diag.relatedInformation === undefined) return;
|
||||
|
||||
const related = find(diag.relatedInformation, related => related.code === Diagnostics.Did_you_mean_0.code);
|
||||
if (related === undefined || related.file === undefined || related.start === undefined || related.length === undefined) return;
|
||||
|
||||
const token = findAncestorMatchingSpan(related.file, createTextSpan(related.start, related.length));
|
||||
if (token === undefined) return;
|
||||
|
||||
if (isExpression(token) && isBinaryExpression(token.parent)) {
|
||||
return { suggestion: getSuggestion(related.messageText), expression: token.parent, arg: token };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, arg: Expression, expression: BinaryExpression) {
|
||||
const callExpression = factory.createCallExpression(
|
||||
factory.createPropertyAccessExpression(factory.createIdentifier("Number"), factory.createIdentifier("isNaN")), /*typeArguments*/ undefined, [arg]);
|
||||
const operator = expression.operatorToken.kind ;
|
||||
changes.replaceNode(sourceFile, expression,
|
||||
operator === SyntaxKind.ExclamationEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken
|
||||
? factory.createPrefixUnaryExpression(SyntaxKind.ExclamationToken, callExpression) : callExpression);
|
||||
}
|
||||
|
||||
function getSuggestion(messageText: string | DiagnosticMessageChain) {
|
||||
const [_, suggestion] = flattenDiagnosticMessageText(messageText, "\n", 0).match(/\'(.*)\'/) || [];
|
||||
return suggestion;
|
||||
}
|
||||
}
|
||||
@ -737,4 +737,13 @@ namespace ts.codefix {
|
||||
export function importSymbols(importAdder: ImportAdder, symbols: readonly Symbol[]) {
|
||||
symbols.forEach(s => importAdder.addImportFromExportedSymbol(s, /*isValidTypeOnlyUseSite*/ true));
|
||||
}
|
||||
|
||||
export function findAncestorMatchingSpan(sourceFile: SourceFile, span: TextSpan): Node {
|
||||
const end = textSpanEnd(span);
|
||||
let token = getTokenAtPosition(sourceFile, span.start);
|
||||
while (token.end < end) {
|
||||
token = token.parent;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@
|
||||
"codefixes/fixConstructorForDerivedNeedSuperCall.ts",
|
||||
"codefixes/fixEnableExperimentalDecorators.ts",
|
||||
"codefixes/fixEnableJsxFlag.ts",
|
||||
"codefixes/fixNaNEquality.ts",
|
||||
"codefixes/fixModuleAndTargetOptions.ts",
|
||||
"codefixes/fixPropertyAssignment.ts",
|
||||
"codefixes/fixExtendsInterfaceBecomesImplements.ts",
|
||||
|
||||
109
tests/baselines/reference/nanEquality.errors.txt
Normal file
109
tests/baselines/reference/nanEquality.errors.txt
Normal file
@ -0,0 +1,109 @@
|
||||
tests/cases/compiler/nanEquality.ts(3,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(4,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(6,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(7,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(9,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(10,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(12,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(13,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(15,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(16,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(18,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(19,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(21,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(22,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(24,5): error TS2845: This condition will always return 'false'.
|
||||
tests/cases/compiler/nanEquality.ts(25,5): error TS2845: This condition will always return 'true'.
|
||||
tests/cases/compiler/nanEquality.ts(29,5): error TS2845: This condition will always return 'false'.
|
||||
|
||||
|
||||
==== tests/cases/compiler/nanEquality.ts (17 errors) ====
|
||||
declare const x: number;
|
||||
|
||||
if (x === NaN) {}
|
||||
~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:3:5: Did you mean 'Number.isNaN(x)'?
|
||||
if (NaN === x) {}
|
||||
~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:4:13: Did you mean 'Number.isNaN(x)'?
|
||||
|
||||
if (x == NaN) {}
|
||||
~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:6:5: Did you mean 'Number.isNaN(x)'?
|
||||
if (NaN == x) {}
|
||||
~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:7:12: Did you mean 'Number.isNaN(x)'?
|
||||
|
||||
if (x !== NaN) {}
|
||||
~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:9:5: Did you mean '!Number.isNaN(x)'?
|
||||
if (NaN !== x) {}
|
||||
~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:10:13: Did you mean '!Number.isNaN(x)'?
|
||||
|
||||
if (x != NaN) {}
|
||||
~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:12:5: Did you mean '!Number.isNaN(x)'?
|
||||
if (NaN != x) {}
|
||||
~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:13:12: Did you mean '!Number.isNaN(x)'?
|
||||
|
||||
if (x === ((NaN))) {}
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:15:5: Did you mean 'Number.isNaN(x)'?
|
||||
if (((NaN)) === x) {}
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:16:17: Did you mean 'Number.isNaN(x)'?
|
||||
|
||||
if (x !== ((NaN))) {}
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:18:5: Did you mean '!Number.isNaN(x)'?
|
||||
if (((NaN)) !== x) {}
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:19:17: Did you mean '!Number.isNaN(x)'?
|
||||
|
||||
if (NaN === NaN) {}
|
||||
~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
if (NaN !== NaN) {}
|
||||
~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
|
||||
if (NaN == NaN) {}
|
||||
~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
if (NaN != NaN) {}
|
||||
~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'true'.
|
||||
|
||||
// ...
|
||||
declare let y: any;
|
||||
if (NaN === y[0][1]) {}
|
||||
~~~~~~~~~~~~~~~
|
||||
!!! error TS2845: This condition will always return 'false'.
|
||||
!!! related TS1369 tests/cases/compiler/nanEquality.ts:29:13: Did you mean 'Number.isNaN(...)'?
|
||||
|
||||
function t1(value: number, NaN: number) {
|
||||
return value === NaN; // ok
|
||||
}
|
||||
|
||||
function t2(value: number, NaN: number) {
|
||||
return NaN == value; // ok
|
||||
}
|
||||
|
||||
function t3(NaN: number) {
|
||||
return NaN === NaN; // ok
|
||||
}
|
||||
|
||||
71
tests/baselines/reference/nanEquality.js
Normal file
71
tests/baselines/reference/nanEquality.js
Normal file
@ -0,0 +1,71 @@
|
||||
//// [nanEquality.ts]
|
||||
declare const x: number;
|
||||
|
||||
if (x === NaN) {}
|
||||
if (NaN === x) {}
|
||||
|
||||
if (x == NaN) {}
|
||||
if (NaN == x) {}
|
||||
|
||||
if (x !== NaN) {}
|
||||
if (NaN !== x) {}
|
||||
|
||||
if (x != NaN) {}
|
||||
if (NaN != x) {}
|
||||
|
||||
if (x === ((NaN))) {}
|
||||
if (((NaN)) === x) {}
|
||||
|
||||
if (x !== ((NaN))) {}
|
||||
if (((NaN)) !== x) {}
|
||||
|
||||
if (NaN === NaN) {}
|
||||
if (NaN !== NaN) {}
|
||||
|
||||
if (NaN == NaN) {}
|
||||
if (NaN != NaN) {}
|
||||
|
||||
// ...
|
||||
declare let y: any;
|
||||
if (NaN === y[0][1]) {}
|
||||
|
||||
function t1(value: number, NaN: number) {
|
||||
return value === NaN; // ok
|
||||
}
|
||||
|
||||
function t2(value: number, NaN: number) {
|
||||
return NaN == value; // ok
|
||||
}
|
||||
|
||||
function t3(NaN: number) {
|
||||
return NaN === NaN; // ok
|
||||
}
|
||||
|
||||
|
||||
//// [nanEquality.js]
|
||||
if (x === NaN) { }
|
||||
if (NaN === x) { }
|
||||
if (x == NaN) { }
|
||||
if (NaN == x) { }
|
||||
if (x !== NaN) { }
|
||||
if (NaN !== x) { }
|
||||
if (x != NaN) { }
|
||||
if (NaN != x) { }
|
||||
if (x === ((NaN))) { }
|
||||
if (((NaN)) === x) { }
|
||||
if (x !== ((NaN))) { }
|
||||
if (((NaN)) !== x) { }
|
||||
if (NaN === NaN) { }
|
||||
if (NaN !== NaN) { }
|
||||
if (NaN == NaN) { }
|
||||
if (NaN != NaN) { }
|
||||
if (NaN === y[0][1]) { }
|
||||
function t1(value, NaN) {
|
||||
return value === NaN; // ok
|
||||
}
|
||||
function t2(value, NaN) {
|
||||
return NaN == value; // ok
|
||||
}
|
||||
function t3(NaN) {
|
||||
return NaN === NaN; // ok
|
||||
}
|
||||
105
tests/baselines/reference/nanEquality.symbols
Normal file
105
tests/baselines/reference/nanEquality.symbols
Normal file
@ -0,0 +1,105 @@
|
||||
=== tests/cases/compiler/nanEquality.ts ===
|
||||
declare const x: number;
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x === NaN) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN === x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x == NaN) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN == x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x !== NaN) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN !== x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x != NaN) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN != x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x === ((NaN))) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (((NaN)) === x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (x !== ((NaN))) {}
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (((NaN)) !== x) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(nanEquality.ts, 0, 13))
|
||||
|
||||
if (NaN === NaN) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN !== NaN) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN == NaN) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
if (NaN != NaN) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
// ...
|
||||
declare let y: any;
|
||||
>y : Symbol(y, Decl(nanEquality.ts, 27, 11))
|
||||
|
||||
if (NaN === y[0][1]) {}
|
||||
>NaN : Symbol(NaN, Decl(lib.es5.d.ts, --, --))
|
||||
>y : Symbol(y, Decl(nanEquality.ts, 27, 11))
|
||||
|
||||
function t1(value: number, NaN: number) {
|
||||
>t1 : Symbol(t1, Decl(nanEquality.ts, 28, 23))
|
||||
>value : Symbol(value, Decl(nanEquality.ts, 30, 12))
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 30, 26))
|
||||
|
||||
return value === NaN; // ok
|
||||
>value : Symbol(value, Decl(nanEquality.ts, 30, 12))
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 30, 26))
|
||||
}
|
||||
|
||||
function t2(value: number, NaN: number) {
|
||||
>t2 : Symbol(t2, Decl(nanEquality.ts, 32, 1))
|
||||
>value : Symbol(value, Decl(nanEquality.ts, 34, 12))
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 34, 26))
|
||||
|
||||
return NaN == value; // ok
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 34, 26))
|
||||
>value : Symbol(value, Decl(nanEquality.ts, 34, 12))
|
||||
}
|
||||
|
||||
function t3(NaN: number) {
|
||||
>t3 : Symbol(t3, Decl(nanEquality.ts, 36, 1))
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
|
||||
|
||||
return NaN === NaN; // ok
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
|
||||
>NaN : Symbol(NaN, Decl(nanEquality.ts, 38, 12))
|
||||
}
|
||||
|
||||
137
tests/baselines/reference/nanEquality.types
Normal file
137
tests/baselines/reference/nanEquality.types
Normal file
@ -0,0 +1,137 @@
|
||||
=== tests/cases/compiler/nanEquality.ts ===
|
||||
declare const x: number;
|
||||
>x : number
|
||||
|
||||
if (x === NaN) {}
|
||||
>x === NaN : boolean
|
||||
>x : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN === x) {}
|
||||
>NaN === x : boolean
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (x == NaN) {}
|
||||
>x == NaN : boolean
|
||||
>x : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN == x) {}
|
||||
>NaN == x : boolean
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (x !== NaN) {}
|
||||
>x !== NaN : boolean
|
||||
>x : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN !== x) {}
|
||||
>NaN !== x : boolean
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (x != NaN) {}
|
||||
>x != NaN : boolean
|
||||
>x : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN != x) {}
|
||||
>NaN != x : boolean
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (x === ((NaN))) {}
|
||||
>x === ((NaN)) : boolean
|
||||
>x : number
|
||||
>((NaN)) : number
|
||||
>(NaN) : number
|
||||
>NaN : number
|
||||
|
||||
if (((NaN)) === x) {}
|
||||
>((NaN)) === x : boolean
|
||||
>((NaN)) : number
|
||||
>(NaN) : number
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (x !== ((NaN))) {}
|
||||
>x !== ((NaN)) : boolean
|
||||
>x : number
|
||||
>((NaN)) : number
|
||||
>(NaN) : number
|
||||
>NaN : number
|
||||
|
||||
if (((NaN)) !== x) {}
|
||||
>((NaN)) !== x : boolean
|
||||
>((NaN)) : number
|
||||
>(NaN) : number
|
||||
>NaN : number
|
||||
>x : number
|
||||
|
||||
if (NaN === NaN) {}
|
||||
>NaN === NaN : boolean
|
||||
>NaN : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN !== NaN) {}
|
||||
>NaN !== NaN : boolean
|
||||
>NaN : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN == NaN) {}
|
||||
>NaN == NaN : boolean
|
||||
>NaN : number
|
||||
>NaN : number
|
||||
|
||||
if (NaN != NaN) {}
|
||||
>NaN != NaN : boolean
|
||||
>NaN : number
|
||||
>NaN : number
|
||||
|
||||
// ...
|
||||
declare let y: any;
|
||||
>y : any
|
||||
|
||||
if (NaN === y[0][1]) {}
|
||||
>NaN === y[0][1] : boolean
|
||||
>NaN : number
|
||||
>y[0][1] : any
|
||||
>y[0] : any
|
||||
>y : any
|
||||
>0 : 0
|
||||
>1 : 1
|
||||
|
||||
function t1(value: number, NaN: number) {
|
||||
>t1 : (value: number, NaN: number) => boolean
|
||||
>value : number
|
||||
>NaN : number
|
||||
|
||||
return value === NaN; // ok
|
||||
>value === NaN : boolean
|
||||
>value : number
|
||||
>NaN : number
|
||||
}
|
||||
|
||||
function t2(value: number, NaN: number) {
|
||||
>t2 : (value: number, NaN: number) => boolean
|
||||
>value : number
|
||||
>NaN : number
|
||||
|
||||
return NaN == value; // ok
|
||||
>NaN == value : boolean
|
||||
>NaN : number
|
||||
>value : number
|
||||
}
|
||||
|
||||
function t3(NaN: number) {
|
||||
>t3 : (NaN: number) => boolean
|
||||
>NaN : number
|
||||
|
||||
return NaN === NaN; // ok
|
||||
>NaN === NaN : boolean
|
||||
>NaN : number
|
||||
>NaN : number
|
||||
}
|
||||
|
||||
41
tests/cases/compiler/nanEquality.ts
Normal file
41
tests/cases/compiler/nanEquality.ts
Normal file
@ -0,0 +1,41 @@
|
||||
declare const x: number;
|
||||
|
||||
if (x === NaN) {}
|
||||
if (NaN === x) {}
|
||||
|
||||
if (x == NaN) {}
|
||||
if (NaN == x) {}
|
||||
|
||||
if (x !== NaN) {}
|
||||
if (NaN !== x) {}
|
||||
|
||||
if (x != NaN) {}
|
||||
if (NaN != x) {}
|
||||
|
||||
if (x === ((NaN))) {}
|
||||
if (((NaN)) === x) {}
|
||||
|
||||
if (x !== ((NaN))) {}
|
||||
if (((NaN)) !== x) {}
|
||||
|
||||
if (NaN === NaN) {}
|
||||
if (NaN !== NaN) {}
|
||||
|
||||
if (NaN == NaN) {}
|
||||
if (NaN != NaN) {}
|
||||
|
||||
// ...
|
||||
declare let y: any;
|
||||
if (NaN === y[0][1]) {}
|
||||
|
||||
function t1(value: number, NaN: number) {
|
||||
return value === NaN; // ok
|
||||
}
|
||||
|
||||
function t2(value: number, NaN: number) {
|
||||
return NaN == value; // ok
|
||||
}
|
||||
|
||||
function t3(NaN: number) {
|
||||
return NaN === NaN; // ok
|
||||
}
|
||||
10
tests/cases/fourslash/fixNaNEquality1.ts
Normal file
10
tests/cases/fourslash/fixNaNEquality1.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: number;
|
||||
////[|if (x === NaN) {}|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Use `Number.isNaN(x)`.",
|
||||
newRangeContent: "if (Number.isNaN(x)) {}",
|
||||
});
|
||||
10
tests/cases/fourslash/fixNaNEquality2.ts
Normal file
10
tests/cases/fourslash/fixNaNEquality2.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: number;
|
||||
////[|if (NaN === x) {}|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Use `Number.isNaN(x)`.",
|
||||
newRangeContent: "if (Number.isNaN(x)) {}",
|
||||
});
|
||||
10
tests/cases/fourslash/fixNaNEquality3.ts
Normal file
10
tests/cases/fourslash/fixNaNEquality3.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: number;
|
||||
////[|if (x !== NaN) {}|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Use `!Number.isNaN(x)`.",
|
||||
newRangeContent: "if (!Number.isNaN(x)) {}",
|
||||
});
|
||||
10
tests/cases/fourslash/fixNaNEquality4.ts
Normal file
10
tests/cases/fourslash/fixNaNEquality4.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: number;
|
||||
////[|if (NaN !== x) {}|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Use `!Number.isNaN(x)`.",
|
||||
newRangeContent: "if (!Number.isNaN(x)) {}",
|
||||
});
|
||||
10
tests/cases/fourslash/fixNaNEquality5.ts
Normal file
10
tests/cases/fourslash/fixNaNEquality5.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: any;
|
||||
////[|if (NaN !== x[0][1]) {}|]
|
||||
|
||||
verify.codeFix({
|
||||
index: 0,
|
||||
description: "Use `!Number.isNaN(...)`.",
|
||||
newRangeContent: "if (!Number.isNaN(x[0][1])) {}",
|
||||
});
|
||||
22
tests/cases/fourslash/fixNaNEquality_all.ts
Normal file
22
tests/cases/fourslash/fixNaNEquality_all.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////declare const x: number;
|
||||
////declare const y: any;
|
||||
////if (x === NaN) {}
|
||||
////if (NaN === x) {}
|
||||
////if (x !== NaN) {}
|
||||
////if (NaN !== x) {}
|
||||
////if (NaN === y[0][1]) {}
|
||||
|
||||
verify.codeFixAll({
|
||||
fixId: "fixNaNEquality",
|
||||
fixAllDescription: ts.Diagnostics.Use_Number_isNaN_in_all_conditions.message,
|
||||
newFileContent:
|
||||
`declare const x: number;
|
||||
declare const y: any;
|
||||
if (Number.isNaN(x)) {}
|
||||
if (Number.isNaN(x)) {}
|
||||
if (!Number.isNaN(x)) {}
|
||||
if (!Number.isNaN(x)) {}
|
||||
if (Number.isNaN(y[0][1])) {}`
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user