Make for..in expressions allowed to be null/undefined (#28348)

This commit is contained in:
Wesley Wigham
2018-11-05 17:23:19 -08:00
committed by GitHub
parent 85dbc0438f
commit 929791868f
8 changed files with 95 additions and 10 deletions

View File

@@ -4766,7 +4766,7 @@ namespace ts {
// A variable declared in a for..in statement is of type string, or of type keyof T when the
// right hand expression is of a type parameter type.
if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForInStatement) {
const indexType = getIndexType(checkNonNullExpression(declaration.parent.parent.expression));
const indexType = getIndexType(getNonNullableTypeIfNeeded(checkExpression(declaration.parent.parent.expression)));
return indexType.flags & (TypeFlags.TypeParameter | TypeFlags.Index) ? getExtractStringType(indexType) : stringType;
}
@@ -15175,6 +15175,10 @@ namespace ts {
}
return declaredType;
}
// for (const _ in ref) acts as a nonnull on ref
if (isVariableDeclaration(node) && node.parent.parent.kind === SyntaxKind.ForInStatement && isMatchingReference(reference, node.parent.parent.expression)) {
return getNonNullableTypeIfNeeded(getTypeFromFlowType(getTypeAtFlowNode(flow.antecedent)));
}
// Assignment doesn't affect reference
return undefined;
}
@@ -18489,6 +18493,14 @@ namespace ts {
);
}
function getNonNullableTypeIfNeeded(type: Type) {
const kind = (strictNullChecks ? getFalsyFlags(type) : type.flags) & TypeFlags.Nullable;
if (kind) {
return getNonNullableType(type);
}
return type;
}
function checkNonNullType(
type: Type,
node: Node,
@@ -25286,7 +25298,7 @@ namespace ts {
// Grammar checking
checkGrammarForInOrForOfStatement(node);
const rightType = checkNonNullExpression(node.expression);
const rightType = getNonNullableTypeIfNeeded(checkExpression(node.expression));
// TypeScript 1.0 spec (April 2014): 5.4
// In a 'for-in' statement of the form
// for (let VarDecl in Expr) Statement

View File

@@ -1,11 +1,10 @@
tests/cases/compiler/ambientWithStatements.ts(2,5): error TS1036: Statements are not allowed in ambient contexts.
tests/cases/compiler/ambientWithStatements.ts(3,5): error TS1104: A 'continue' statement can only be used within an enclosing iteration statement.
tests/cases/compiler/ambientWithStatements.ts(7,15): error TS2531: Object is possibly 'null'.
tests/cases/compiler/ambientWithStatements.ts(11,5): error TS1108: A 'return' statement can only be used within a function body.
tests/cases/compiler/ambientWithStatements.ts(25,5): error TS2410: The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.
==== tests/cases/compiler/ambientWithStatements.ts (5 errors) ====
==== tests/cases/compiler/ambientWithStatements.ts (4 errors) ====
declare module M {
break;
~~~~~
@@ -17,8 +16,6 @@ tests/cases/compiler/ambientWithStatements.ts(25,5): error TS2410: The 'with' st
do { } while (true);
var x;
for (x in null) { }
~~~~
!!! error TS2531: Object is possibly 'null'.
if (true) { } else { }
1;
L: var y;

View File

@@ -0,0 +1,12 @@
tests/cases/compiler/forInStrictNullChecksNoError.ts(5,5): error TS2533: Object is possibly 'null' or 'undefined'.
==== tests/cases/compiler/forInStrictNullChecksNoError.ts (1 errors) ====
function f(x: { [key: string]: number; } | null | undefined) {
for (const key in x) { // 1
console.log(x[key]); // 2
}
x["no"]; // should still error
~
!!! error TS2533: Object is possibly 'null' or 'undefined'.
}

View File

@@ -0,0 +1,15 @@
//// [forInStrictNullChecksNoError.ts]
function f(x: { [key: string]: number; } | null | undefined) {
for (const key in x) { // 1
console.log(x[key]); // 2
}
x["no"]; // should still error
}
//// [forInStrictNullChecksNoError.js]
function f(x) {
for (var key in x) { // 1
console.log(x[key]); // 2
}
x["no"]; // should still error
}

View File

@@ -0,0 +1,20 @@
=== tests/cases/compiler/forInStrictNullChecksNoError.ts ===
function f(x: { [key: string]: number; } | null | undefined) {
>f : Symbol(f, Decl(forInStrictNullChecksNoError.ts, 0, 0))
>x : Symbol(x, Decl(forInStrictNullChecksNoError.ts, 0, 11))
>key : Symbol(key, Decl(forInStrictNullChecksNoError.ts, 0, 17))
for (const key in x) { // 1
>key : Symbol(key, Decl(forInStrictNullChecksNoError.ts, 1, 14))
>x : Symbol(x, Decl(forInStrictNullChecksNoError.ts, 0, 11))
console.log(x[key]); // 2
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>x : Symbol(x, Decl(forInStrictNullChecksNoError.ts, 0, 11))
>key : Symbol(key, Decl(forInStrictNullChecksNoError.ts, 1, 14))
}
x["no"]; // should still error
>x : Symbol(x, Decl(forInStrictNullChecksNoError.ts, 0, 11))
}

View File

@@ -0,0 +1,25 @@
=== tests/cases/compiler/forInStrictNullChecksNoError.ts ===
function f(x: { [key: string]: number; } | null | undefined) {
>f : (x: { [key: string]: number; } | null | undefined) => void
>x : { [key: string]: number; } | null | undefined
>key : string
>null : null
for (const key in x) { // 1
>key : string
>x : { [key: string]: number; } | null | undefined
console.log(x[key]); // 2
>console.log(x[key]) : void
>console.log : (message?: any, ...optionalParams: any[]) => void
>console : Console
>log : (message?: any, ...optionalParams: any[]) => void
>x[key] : number
>x : { [key: string]: number; }
>key : string
}
x["no"]; // should still error
>x["no"] : number
>x : { [key: string]: number; } | null | undefined
>"no" : "no"
}

View File

@@ -1,7 +1,6 @@
tests/cases/compiler/widenedTypes.ts(1,1): error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.
tests/cases/compiler/widenedTypes.ts(4,1): error TS2531: Object is possibly 'null'.
tests/cases/compiler/widenedTypes.ts(5,7): error TS2531: Object is possibly 'null'.
tests/cases/compiler/widenedTypes.ts(7,15): error TS2531: Object is possibly 'null'.
tests/cases/compiler/widenedTypes.ts(9,14): error TS2695: Left side of comma operator is unused and has no side effects.
tests/cases/compiler/widenedTypes.ts(10,1): error TS2322: Type '""' is not assignable to type 'number'.
tests/cases/compiler/widenedTypes.ts(17,1): error TS2322: Type '""' is not assignable to type 'number'.
@@ -9,7 +8,7 @@ tests/cases/compiler/widenedTypes.ts(22,22): error TS2322: Type 'number' is not
tests/cases/compiler/widenedTypes.ts(23,39): error TS2322: Type 'number' is not assignable to type 'string'.
==== tests/cases/compiler/widenedTypes.ts (9 errors) ====
==== tests/cases/compiler/widenedTypes.ts (8 errors) ====
null instanceof (() => { });
~~~~
!!! error TS2358: The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.
@@ -23,8 +22,6 @@ tests/cases/compiler/widenedTypes.ts(23,39): error TS2322: Type 'number' is not
!!! error TS2531: Object is possibly 'null'.
for (var a in null) { }
~~~~
!!! error TS2531: Object is possibly 'null'.
var t = [3, (3, null)];
~

View File

@@ -0,0 +1,7 @@
// @strictNullChecks: true
function f(x: { [key: string]: number; } | null | undefined) {
for (const key in x) { // 1
console.log(x[key]); // 2
}
x["no"]; // should still error
}