Allow to narrow the type of an import (#16658)
* Allow to narrow the type of an import * Assume alias is initialized
This commit is contained in:
parent
ff5d245dcb
commit
12163cc02e
|
@ -12009,9 +12009,9 @@ namespace ts {
|
|||
}
|
||||
|
||||
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
|
||||
let declaration = localOrExportSymbol.valueDeclaration;
|
||||
|
||||
if (localOrExportSymbol.flags & SymbolFlags.Class) {
|
||||
const declaration = localOrExportSymbol.valueDeclaration;
|
||||
// Due to the emit for class decorators, any reference to the class from inside of the class body
|
||||
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
|
||||
// behavior of class names in ES6.
|
||||
|
@ -12053,7 +12053,6 @@ namespace ts {
|
|||
checkNestedBlockScopedBinding(node, symbol);
|
||||
|
||||
const type = getDeclaredOrApparentType(localOrExportSymbol, node);
|
||||
const declaration = localOrExportSymbol.valueDeclaration;
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
|
||||
if (assignmentKind) {
|
||||
|
@ -12067,11 +12066,26 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
const isAlias = localOrExportSymbol.flags & SymbolFlags.Alias;
|
||||
|
||||
// We only narrow variables and parameters occurring in a non-assignment position. For all other
|
||||
// entities we simply return the declared type.
|
||||
if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || assignmentKind === AssignmentKind.Definite || !declaration) {
|
||||
if (localOrExportSymbol.flags & SymbolFlags.Variable) {
|
||||
if (assignmentKind === AssignmentKind.Definite) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
else if (isAlias) {
|
||||
declaration = find<Declaration>(symbol.declarations, isSomeImportDeclaration);
|
||||
}
|
||||
else {
|
||||
return type;
|
||||
}
|
||||
|
||||
if (!declaration) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// The declaration container is the innermost function that encloses the declaration of the variable
|
||||
// or parameter. The flow container is the innermost function starting with which we analyze the control
|
||||
// flow graph to determine the control flow based type.
|
||||
|
@ -12090,7 +12104,7 @@ namespace ts {
|
|||
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
|
||||
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
|
||||
// declaration container are the same).
|
||||
const assumeInitialized = isParameter || isOuterVariable ||
|
||||
const assumeInitialized = isParameter || isAlias || isOuterVariable ||
|
||||
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
|
||||
node.parent.kind === SyntaxKind.NonNullExpression ||
|
||||
isInAmbientContext(declaration);
|
||||
|
@ -24776,4 +24790,19 @@ namespace ts {
|
|||
return isDeclarationName(name);
|
||||
}
|
||||
}
|
||||
|
||||
function isSomeImportDeclaration(decl: Node): boolean {
|
||||
switch (decl.kind) {
|
||||
case SyntaxKind.ImportClause: // For default import
|
||||
case SyntaxKind.ImportEqualsDeclaration:
|
||||
case SyntaxKind.NamespaceImport:
|
||||
case SyntaxKind.ImportSpecifier: // For rename import `x as y`
|
||||
return true;
|
||||
case SyntaxKind.Identifier:
|
||||
// For regular import, `decl` is an Identifier under the ImportSpecifier.
|
||||
return decl.parent.kind === SyntaxKind.ImportSpecifier;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
43
tests/baselines/reference/narrowedImports.js
Normal file
43
tests/baselines/reference/narrowedImports.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
//// [tests/cases/compiler/narrowedImports.ts] ////
|
||||
|
||||
//// [a.d.ts]
|
||||
declare const a0: number | undefined;
|
||||
export default a0;
|
||||
export const a1: number | undefined;
|
||||
|
||||
//// [b.d.ts]
|
||||
declare const b: number | undefined;
|
||||
declare namespace b {}
|
||||
export = b;
|
||||
|
||||
//// [x.ts]
|
||||
import a0, { a1, a1 as a2 } from "./a";
|
||||
import * as b0 from "./b";
|
||||
import b1 = require("./b");
|
||||
|
||||
let x: number;
|
||||
|
||||
if (a0) x = a0;
|
||||
if (a1) x = a1;
|
||||
if (a2) x = a2;
|
||||
if (b0) x = b0;
|
||||
if (b1) x = b1;
|
||||
|
||||
|
||||
//// [x.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var a_1 = require("./a");
|
||||
var b0 = require("./b");
|
||||
var b1 = require("./b");
|
||||
var x;
|
||||
if (a_1["default"])
|
||||
x = a_1["default"];
|
||||
if (a_1.a1)
|
||||
x = a_1.a1;
|
||||
if (a_1.a1)
|
||||
x = a_1.a1;
|
||||
if (b0)
|
||||
x = b0;
|
||||
if (b1)
|
||||
x = b1;
|
61
tests/baselines/reference/narrowedImports.symbols
Normal file
61
tests/baselines/reference/narrowedImports.symbols
Normal file
|
@ -0,0 +1,61 @@
|
|||
=== /x.ts ===
|
||||
import a0, { a1, a1 as a2 } from "./a";
|
||||
>a0 : Symbol(a0, Decl(x.ts, 0, 6))
|
||||
>a1 : Symbol(a1, Decl(x.ts, 0, 12))
|
||||
>a1 : Symbol(a2, Decl(x.ts, 0, 16))
|
||||
>a2 : Symbol(a2, Decl(x.ts, 0, 16))
|
||||
|
||||
import * as b0 from "./b";
|
||||
>b0 : Symbol(b0, Decl(x.ts, 1, 6))
|
||||
|
||||
import b1 = require("./b");
|
||||
>b1 : Symbol(b1, Decl(x.ts, 1, 26))
|
||||
|
||||
let x: number;
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
|
||||
if (a0) x = a0;
|
||||
>a0 : Symbol(a0, Decl(x.ts, 0, 6))
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
>a0 : Symbol(a0, Decl(x.ts, 0, 6))
|
||||
|
||||
if (a1) x = a1;
|
||||
>a1 : Symbol(a1, Decl(x.ts, 0, 12))
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
>a1 : Symbol(a1, Decl(x.ts, 0, 12))
|
||||
|
||||
if (a2) x = a2;
|
||||
>a2 : Symbol(a2, Decl(x.ts, 0, 16))
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
>a2 : Symbol(a2, Decl(x.ts, 0, 16))
|
||||
|
||||
if (b0) x = b0;
|
||||
>b0 : Symbol(b0, Decl(x.ts, 1, 6))
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
>b0 : Symbol(b0, Decl(x.ts, 1, 6))
|
||||
|
||||
if (b1) x = b1;
|
||||
>b1 : Symbol(b1, Decl(x.ts, 1, 26))
|
||||
>x : Symbol(x, Decl(x.ts, 4, 3))
|
||||
>b1 : Symbol(b1, Decl(x.ts, 1, 26))
|
||||
|
||||
=== /a.d.ts ===
|
||||
declare const a0: number | undefined;
|
||||
>a0 : Symbol(a0, Decl(a.d.ts, 0, 13))
|
||||
|
||||
export default a0;
|
||||
>a0 : Symbol(a0, Decl(a.d.ts, 0, 13))
|
||||
|
||||
export const a1: number | undefined;
|
||||
>a1 : Symbol(a1, Decl(a.d.ts, 2, 12))
|
||||
|
||||
=== /b.d.ts ===
|
||||
declare const b: number | undefined;
|
||||
>b : Symbol(b, Decl(b.d.ts, 0, 13), Decl(b.d.ts, 0, 36))
|
||||
|
||||
declare namespace b {}
|
||||
>b : Symbol(b, Decl(b.d.ts, 0, 13), Decl(b.d.ts, 0, 36))
|
||||
|
||||
export = b;
|
||||
>b : Symbol(b, Decl(b.d.ts, 0, 13), Decl(b.d.ts, 0, 36))
|
||||
|
66
tests/baselines/reference/narrowedImports.types
Normal file
66
tests/baselines/reference/narrowedImports.types
Normal file
|
@ -0,0 +1,66 @@
|
|||
=== /x.ts ===
|
||||
import a0, { a1, a1 as a2 } from "./a";
|
||||
>a0 : number | undefined
|
||||
>a1 : number | undefined
|
||||
>a1 : number | undefined
|
||||
>a2 : number | undefined
|
||||
|
||||
import * as b0 from "./b";
|
||||
>b0 : number | undefined
|
||||
|
||||
import b1 = require("./b");
|
||||
>b1 : number | undefined
|
||||
|
||||
let x: number;
|
||||
>x : number
|
||||
|
||||
if (a0) x = a0;
|
||||
>a0 : number | undefined
|
||||
>x = a0 : number
|
||||
>x : number
|
||||
>a0 : number
|
||||
|
||||
if (a1) x = a1;
|
||||
>a1 : number | undefined
|
||||
>x = a1 : number
|
||||
>x : number
|
||||
>a1 : number
|
||||
|
||||
if (a2) x = a2;
|
||||
>a2 : number | undefined
|
||||
>x = a2 : number
|
||||
>x : number
|
||||
>a2 : number
|
||||
|
||||
if (b0) x = b0;
|
||||
>b0 : number | undefined
|
||||
>x = b0 : number
|
||||
>x : number
|
||||
>b0 : number
|
||||
|
||||
if (b1) x = b1;
|
||||
>b1 : number | undefined
|
||||
>x = b1 : number
|
||||
>x : number
|
||||
>b1 : number
|
||||
|
||||
=== /a.d.ts ===
|
||||
declare const a0: number | undefined;
|
||||
>a0 : number | undefined
|
||||
|
||||
export default a0;
|
||||
>a0 : number | undefined
|
||||
|
||||
export const a1: number | undefined;
|
||||
>a1 : number | undefined
|
||||
|
||||
=== /b.d.ts ===
|
||||
declare const b: number | undefined;
|
||||
>b : number | undefined
|
||||
|
||||
declare namespace b {}
|
||||
>b : number | undefined
|
||||
|
||||
export = b;
|
||||
>b : number | undefined
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
//// [tests/cases/compiler/narrowedImports_assumeInitialized.ts] ////
|
||||
|
||||
//// [a.d.ts]
|
||||
declare namespace a {
|
||||
export const x: number;
|
||||
}
|
||||
export = a;
|
||||
|
||||
//// [b.ts]
|
||||
import a = require("./a");
|
||||
a.x;
|
||||
|
||||
|
||||
//// [b.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
var a = require("./a");
|
||||
a.x;
|
|
@ -0,0 +1,19 @@
|
|||
=== /b.ts ===
|
||||
import a = require("./a");
|
||||
>a : Symbol(a, Decl(b.ts, 0, 0))
|
||||
|
||||
a.x;
|
||||
>a.x : Symbol(a.x, Decl(a.d.ts, 1, 16))
|
||||
>a : Symbol(a, Decl(b.ts, 0, 0))
|
||||
>x : Symbol(a.x, Decl(a.d.ts, 1, 16))
|
||||
|
||||
=== /a.d.ts ===
|
||||
declare namespace a {
|
||||
>a : Symbol(a, Decl(a.d.ts, 0, 0))
|
||||
|
||||
export const x: number;
|
||||
>x : Symbol(x, Decl(a.d.ts, 1, 16))
|
||||
}
|
||||
export = a;
|
||||
>a : Symbol(a, Decl(a.d.ts, 0, 0))
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
=== /b.ts ===
|
||||
import a = require("./a");
|
||||
>a : typeof a
|
||||
|
||||
a.x;
|
||||
>a.x : number
|
||||
>a : typeof a
|
||||
>x : number
|
||||
|
||||
=== /a.d.ts ===
|
||||
declare namespace a {
|
||||
>a : typeof a
|
||||
|
||||
export const x: number;
|
||||
>x : number
|
||||
}
|
||||
export = a;
|
||||
>a : typeof a
|
||||
|
24
tests/cases/compiler/narrowedImports.ts
Normal file
24
tests/cases/compiler/narrowedImports.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
// @strictNullChecks: true
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
declare const a0: number | undefined;
|
||||
export default a0;
|
||||
export const a1: number | undefined;
|
||||
|
||||
// @Filename: /b.d.ts
|
||||
declare const b: number | undefined;
|
||||
declare namespace b {}
|
||||
export = b;
|
||||
|
||||
// @Filename: /x.ts
|
||||
import a0, { a1, a1 as a2 } from "./a";
|
||||
import * as b0 from "./b";
|
||||
import b1 = require("./b");
|
||||
|
||||
let x: number;
|
||||
|
||||
if (a0) x = a0;
|
||||
if (a1) x = a1;
|
||||
if (a2) x = a2;
|
||||
if (b0) x = b0;
|
||||
if (b1) x = b1;
|
11
tests/cases/compiler/narrowedImports_assumeInitialized.ts
Normal file
11
tests/cases/compiler/narrowedImports_assumeInitialized.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
// @strictNullChecks: true
|
||||
|
||||
// @Filename: /a.d.ts
|
||||
declare namespace a {
|
||||
export const x: number;
|
||||
}
|
||||
export = a;
|
||||
|
||||
// @Filename: /b.ts
|
||||
import a = require("./a");
|
||||
a.x;
|
Loading…
Reference in a new issue