mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-08 02:46:15 -05:00
Make for..in expressions allowed to be null/undefined (#28348)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'.
|
||||
}
|
||||
15
tests/baselines/reference/forInStrictNullChecksNoError.js
Normal file
15
tests/baselines/reference/forInStrictNullChecksNoError.js
Normal 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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
25
tests/baselines/reference/forInStrictNullChecksNoError.types
Normal file
25
tests/baselines/reference/forInStrictNullChecksNoError.types
Normal 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"
|
||||
}
|
||||
@@ -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)];
|
||||
~
|
||||
|
||||
7
tests/cases/compiler/forInStrictNullChecksNoError.ts
Normal file
7
tests/cases/compiler/forInStrictNullChecksNoError.ts
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user