mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-10 01:43:59 -05:00
Merge pull request #15576 from Microsoft/typeVariableTypeGuards
Improve type guards for type variables
This commit is contained in:
@@ -5775,11 +5775,11 @@ namespace ts {
|
||||
const t = type.flags & TypeFlags.TypeVariable ? getBaseConstraintOfType(type) || emptyObjectType : type;
|
||||
return t.flags & TypeFlags.Intersection ? getApparentTypeOfIntersectionType(<IntersectionType>t) :
|
||||
t.flags & TypeFlags.StringLike ? globalStringType :
|
||||
t.flags & TypeFlags.NumberLike ? globalNumberType :
|
||||
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
|
||||
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
|
||||
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
|
||||
t;
|
||||
t.flags & TypeFlags.NumberLike ? globalNumberType :
|
||||
t.flags & TypeFlags.BooleanLike ? globalBooleanType :
|
||||
t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*reportErrors*/ languageVersion >= ScriptTarget.ES2015) :
|
||||
t.flags & TypeFlags.NonPrimitive ? emptyObjectType :
|
||||
t;
|
||||
}
|
||||
|
||||
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
|
||||
@@ -10441,11 +10441,13 @@ namespace ts {
|
||||
// Return the flow cache key for a "dotted name" (i.e. a sequence of identifiers
|
||||
// separated by dots). The key consists of the id of the symbol referenced by the
|
||||
// leftmost identifier followed by zero or more property names separated by dots.
|
||||
// The result is undefined if the reference isn't a dotted name.
|
||||
// The result is undefined if the reference isn't a dotted name. We prefix nodes
|
||||
// occurring in an apparent type position with '@' because the control flow type
|
||||
// of such nodes may be based on the apparent type instead of the declared type.
|
||||
function getFlowCacheKey(node: Node): string {
|
||||
if (node.kind === SyntaxKind.Identifier) {
|
||||
const symbol = getResolvedSymbol(<Identifier>node);
|
||||
return symbol !== unknownSymbol ? "" + getSymbolId(symbol) : undefined;
|
||||
return symbol !== unknownSymbol ? (isApparentTypePosition(node) ? "@" : "") + getSymbolId(symbol) : undefined;
|
||||
}
|
||||
if (node.kind === SyntaxKind.ThisKeyword) {
|
||||
return "0";
|
||||
@@ -11710,6 +11712,29 @@ namespace ts {
|
||||
return annotationIncludesUndefined ? getTypeWithFacts(declaredType, TypeFacts.NEUndefined) : declaredType;
|
||||
}
|
||||
|
||||
function isApparentTypePosition(node: Node) {
|
||||
const parent = node.parent;
|
||||
return parent.kind === SyntaxKind.PropertyAccessExpression ||
|
||||
parent.kind === SyntaxKind.CallExpression && (<CallExpression>parent).expression === node ||
|
||||
parent.kind === SyntaxKind.ElementAccessExpression && (<ElementAccessExpression>parent).expression === node;
|
||||
}
|
||||
|
||||
function typeHasNullableConstraint(type: Type) {
|
||||
return type.flags & TypeFlags.TypeVariable && maybeTypeOfKind(getBaseConstraintOfType(type) || emptyObjectType, TypeFlags.Nullable);
|
||||
}
|
||||
|
||||
function getDeclaredOrApparentType(symbol: Symbol, node: Node) {
|
||||
// When a node is the left hand expression of a property access, element access, or call expression,
|
||||
// and the type of the node includes type variables with constraints that are nullable, we fetch the
|
||||
// apparent type of the node *before* performing control flow analysis such that narrowings apply to
|
||||
// the constraint type.
|
||||
const type = getTypeOfSymbol(symbol);
|
||||
if (isApparentTypePosition(node) && forEachType(type, typeHasNullableConstraint)) {
|
||||
return mapType(getWidenedType(type), getApparentType);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function checkIdentifier(node: Identifier): Type {
|
||||
const symbol = getResolvedSymbol(node);
|
||||
if (symbol === unknownSymbol) {
|
||||
@@ -11785,7 +11810,7 @@ namespace ts {
|
||||
checkCollisionWithCapturedNewTargetVariable(node, node);
|
||||
checkNestedBlockScopedBinding(node, symbol);
|
||||
|
||||
const type = getTypeOfSymbol(localOrExportSymbol);
|
||||
const type = getDeclaredOrApparentType(localOrExportSymbol, node);
|
||||
const declaration = localOrExportSymbol.valueDeclaration;
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
|
||||
@@ -14158,7 +14183,7 @@ namespace ts {
|
||||
|
||||
checkPropertyAccessibility(node, left, apparentType, prop);
|
||||
|
||||
const propType = getTypeOfSymbol(prop);
|
||||
const propType = getDeclaredOrApparentType(prop, node);
|
||||
const assignmentKind = getAssignmentTargetKind(node);
|
||||
|
||||
if (assignmentKind) {
|
||||
|
||||
158
tests/baselines/reference/typeVariableTypeGuards.js
Normal file
158
tests/baselines/reference/typeVariableTypeGuards.js
Normal file
@@ -0,0 +1,158 @@
|
||||
//// [typeVariableTypeGuards.ts]
|
||||
// Repro from #14091
|
||||
|
||||
interface Foo {
|
||||
foo(): void
|
||||
}
|
||||
|
||||
class A<P extends Partial<Foo>> {
|
||||
props: Readonly<P>
|
||||
doSomething() {
|
||||
this.props.foo && this.props.foo()
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #14415
|
||||
|
||||
interface Banana {
|
||||
color: 'yellow';
|
||||
}
|
||||
|
||||
class Monkey<T extends Banana | undefined> {
|
||||
a: T;
|
||||
render() {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BigBanana extends Banana {
|
||||
}
|
||||
|
||||
class BigMonkey extends Monkey<BigBanana> {
|
||||
render() {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another repro
|
||||
|
||||
type Item = {
|
||||
(): string;
|
||||
x: string;
|
||||
}
|
||||
|
||||
function f1<T extends Item | undefined>(obj: T) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f2<T extends Item | undefined>(obj: T | undefined) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f3<T extends Item | undefined>(obj: T | null) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f4<T extends string[] | undefined>(obj: T | undefined, x: number) {
|
||||
if (obj) {
|
||||
obj[x].length;
|
||||
}
|
||||
}
|
||||
|
||||
function f5<T, K extends keyof T>(obj: T | undefined, key: K) {
|
||||
if (obj) {
|
||||
obj[key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [typeVariableTypeGuards.js]
|
||||
"use strict";
|
||||
// Repro from #14091
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var A = (function () {
|
||||
function A() {
|
||||
}
|
||||
A.prototype.doSomething = function () {
|
||||
this.props.foo && this.props.foo();
|
||||
};
|
||||
return A;
|
||||
}());
|
||||
var Monkey = (function () {
|
||||
function Monkey() {
|
||||
}
|
||||
Monkey.prototype.render = function () {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
};
|
||||
return Monkey;
|
||||
}());
|
||||
var BigMonkey = (function (_super) {
|
||||
__extends(BigMonkey, _super);
|
||||
function BigMonkey() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
BigMonkey.prototype.render = function () {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
};
|
||||
return BigMonkey;
|
||||
}(Monkey));
|
||||
function f1(obj) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
function f2(obj) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
function f3(obj) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
function f4(obj, x) {
|
||||
if (obj) {
|
||||
obj[x].length;
|
||||
}
|
||||
}
|
||||
function f5(obj, key) {
|
||||
if (obj) {
|
||||
obj[key];
|
||||
}
|
||||
}
|
||||
221
tests/baselines/reference/typeVariableTypeGuards.symbols
Normal file
221
tests/baselines/reference/typeVariableTypeGuards.symbols
Normal file
@@ -0,0 +1,221 @@
|
||||
=== tests/cases/compiler/typeVariableTypeGuards.ts ===
|
||||
// Repro from #14091
|
||||
|
||||
interface Foo {
|
||||
>Foo : Symbol(Foo, Decl(typeVariableTypeGuards.ts, 0, 0))
|
||||
|
||||
foo(): void
|
||||
>foo : Symbol(Foo.foo, Decl(typeVariableTypeGuards.ts, 2, 15))
|
||||
}
|
||||
|
||||
class A<P extends Partial<Foo>> {
|
||||
>A : Symbol(A, Decl(typeVariableTypeGuards.ts, 4, 1))
|
||||
>P : Symbol(P, Decl(typeVariableTypeGuards.ts, 6, 8))
|
||||
>Partial : Symbol(Partial, Decl(lib.d.ts, --, --))
|
||||
>Foo : Symbol(Foo, Decl(typeVariableTypeGuards.ts, 0, 0))
|
||||
|
||||
props: Readonly<P>
|
||||
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
|
||||
>Readonly : Symbol(Readonly, Decl(lib.d.ts, --, --))
|
||||
>P : Symbol(P, Decl(typeVariableTypeGuards.ts, 6, 8))
|
||||
|
||||
doSomething() {
|
||||
>doSomething : Symbol(A.doSomething, Decl(typeVariableTypeGuards.ts, 7, 22))
|
||||
|
||||
this.props.foo && this.props.foo()
|
||||
>this.props.foo : Symbol(foo)
|
||||
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
|
||||
>this : Symbol(A, Decl(typeVariableTypeGuards.ts, 4, 1))
|
||||
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
|
||||
>foo : Symbol(foo)
|
||||
>this.props.foo : Symbol(foo)
|
||||
>this.props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
|
||||
>this : Symbol(A, Decl(typeVariableTypeGuards.ts, 4, 1))
|
||||
>props : Symbol(A.props, Decl(typeVariableTypeGuards.ts, 6, 33))
|
||||
>foo : Symbol(foo)
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #14415
|
||||
|
||||
interface Banana {
|
||||
>Banana : Symbol(Banana, Decl(typeVariableTypeGuards.ts, 11, 1))
|
||||
|
||||
color: 'yellow';
|
||||
>color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
|
||||
}
|
||||
|
||||
class Monkey<T extends Banana | undefined> {
|
||||
>Monkey : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 19, 13))
|
||||
>Banana : Symbol(Banana, Decl(typeVariableTypeGuards.ts, 11, 1))
|
||||
|
||||
a: T;
|
||||
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 19, 13))
|
||||
|
||||
render() {
|
||||
>render : Symbol(Monkey.render, Decl(typeVariableTypeGuards.ts, 20, 9))
|
||||
|
||||
if (this.a) {
|
||||
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>this : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
|
||||
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
|
||||
this.a.color;
|
||||
>this.a.color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
|
||||
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>this : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
|
||||
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BigBanana extends Banana {
|
||||
>BigBanana : Symbol(BigBanana, Decl(typeVariableTypeGuards.ts, 26, 1))
|
||||
>Banana : Symbol(Banana, Decl(typeVariableTypeGuards.ts, 11, 1))
|
||||
}
|
||||
|
||||
class BigMonkey extends Monkey<BigBanana> {
|
||||
>BigMonkey : Symbol(BigMonkey, Decl(typeVariableTypeGuards.ts, 29, 1))
|
||||
>Monkey : Symbol(Monkey, Decl(typeVariableTypeGuards.ts, 17, 1))
|
||||
>BigBanana : Symbol(BigBanana, Decl(typeVariableTypeGuards.ts, 26, 1))
|
||||
|
||||
render() {
|
||||
>render : Symbol(BigMonkey.render, Decl(typeVariableTypeGuards.ts, 31, 43))
|
||||
|
||||
if (this.a) {
|
||||
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>this : Symbol(BigMonkey, Decl(typeVariableTypeGuards.ts, 29, 1))
|
||||
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
|
||||
this.a.color;
|
||||
>this.a.color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
|
||||
>this.a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>this : Symbol(BigMonkey, Decl(typeVariableTypeGuards.ts, 29, 1))
|
||||
>a : Symbol(Monkey.a, Decl(typeVariableTypeGuards.ts, 19, 44))
|
||||
>color : Symbol(Banana.color, Decl(typeVariableTypeGuards.ts, 15, 18))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another repro
|
||||
|
||||
type Item = {
|
||||
>Item : Symbol(Item, Decl(typeVariableTypeGuards.ts, 37, 1))
|
||||
|
||||
(): string;
|
||||
x: string;
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
}
|
||||
|
||||
function f1<T extends Item | undefined>(obj: T) {
|
||||
>f1 : Symbol(f1, Decl(typeVariableTypeGuards.ts, 44, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 46, 12))
|
||||
>Item : Symbol(Item, Decl(typeVariableTypeGuards.ts, 37, 1))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 46, 40))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 46, 12))
|
||||
|
||||
if (obj) {
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 46, 40))
|
||||
|
||||
obj.x;
|
||||
>obj.x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 46, 40))
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj["x"];
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 46, 40))
|
||||
>"x" : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj();
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 46, 40))
|
||||
}
|
||||
}
|
||||
|
||||
function f2<T extends Item | undefined>(obj: T | undefined) {
|
||||
>f2 : Symbol(f2, Decl(typeVariableTypeGuards.ts, 52, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 54, 12))
|
||||
>Item : Symbol(Item, Decl(typeVariableTypeGuards.ts, 37, 1))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 54, 40))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 54, 12))
|
||||
|
||||
if (obj) {
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 54, 40))
|
||||
|
||||
obj.x;
|
||||
>obj.x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 54, 40))
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj["x"];
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 54, 40))
|
||||
>"x" : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj();
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 54, 40))
|
||||
}
|
||||
}
|
||||
|
||||
function f3<T extends Item | undefined>(obj: T | null) {
|
||||
>f3 : Symbol(f3, Decl(typeVariableTypeGuards.ts, 60, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 62, 12))
|
||||
>Item : Symbol(Item, Decl(typeVariableTypeGuards.ts, 37, 1))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 62, 40))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 62, 12))
|
||||
|
||||
if (obj) {
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 62, 40))
|
||||
|
||||
obj.x;
|
||||
>obj.x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 62, 40))
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj["x"];
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 62, 40))
|
||||
>"x" : Symbol(x, Decl(typeVariableTypeGuards.ts, 42, 15))
|
||||
|
||||
obj();
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 62, 40))
|
||||
}
|
||||
}
|
||||
|
||||
function f4<T extends string[] | undefined>(obj: T | undefined, x: number) {
|
||||
>f4 : Symbol(f4, Decl(typeVariableTypeGuards.ts, 68, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 70, 12))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 70, 44))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 70, 12))
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 70, 63))
|
||||
|
||||
if (obj) {
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 70, 44))
|
||||
|
||||
obj[x].length;
|
||||
>obj[x].length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 70, 44))
|
||||
>x : Symbol(x, Decl(typeVariableTypeGuards.ts, 70, 63))
|
||||
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
|
||||
function f5<T, K extends keyof T>(obj: T | undefined, key: K) {
|
||||
>f5 : Symbol(f5, Decl(typeVariableTypeGuards.ts, 74, 1))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 76, 12))
|
||||
>K : Symbol(K, Decl(typeVariableTypeGuards.ts, 76, 14))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 76, 12))
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 76, 34))
|
||||
>T : Symbol(T, Decl(typeVariableTypeGuards.ts, 76, 12))
|
||||
>key : Symbol(key, Decl(typeVariableTypeGuards.ts, 76, 53))
|
||||
>K : Symbol(K, Decl(typeVariableTypeGuards.ts, 76, 14))
|
||||
|
||||
if (obj) {
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 76, 34))
|
||||
|
||||
obj[key];
|
||||
>obj : Symbol(obj, Decl(typeVariableTypeGuards.ts, 76, 34))
|
||||
>key : Symbol(key, Decl(typeVariableTypeGuards.ts, 76, 53))
|
||||
}
|
||||
}
|
||||
|
||||
232
tests/baselines/reference/typeVariableTypeGuards.types
Normal file
232
tests/baselines/reference/typeVariableTypeGuards.types
Normal file
@@ -0,0 +1,232 @@
|
||||
=== tests/cases/compiler/typeVariableTypeGuards.ts ===
|
||||
// Repro from #14091
|
||||
|
||||
interface Foo {
|
||||
>Foo : Foo
|
||||
|
||||
foo(): void
|
||||
>foo : () => void
|
||||
}
|
||||
|
||||
class A<P extends Partial<Foo>> {
|
||||
>A : A<P>
|
||||
>P : P
|
||||
>Partial : Partial<T>
|
||||
>Foo : Foo
|
||||
|
||||
props: Readonly<P>
|
||||
>props : Readonly<P>
|
||||
>Readonly : Readonly<T>
|
||||
>P : P
|
||||
|
||||
doSomething() {
|
||||
>doSomething : () => void
|
||||
|
||||
this.props.foo && this.props.foo()
|
||||
>this.props.foo && this.props.foo() : void
|
||||
>this.props.foo : P["foo"]
|
||||
>this.props : Readonly<P>
|
||||
>this : this
|
||||
>props : Readonly<P>
|
||||
>foo : P["foo"]
|
||||
>this.props.foo() : void
|
||||
>this.props.foo : () => void
|
||||
>this.props : Readonly<P>
|
||||
>this : this
|
||||
>props : Readonly<P>
|
||||
>foo : () => void
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #14415
|
||||
|
||||
interface Banana {
|
||||
>Banana : Banana
|
||||
|
||||
color: 'yellow';
|
||||
>color : "yellow"
|
||||
}
|
||||
|
||||
class Monkey<T extends Banana | undefined> {
|
||||
>Monkey : Monkey<T>
|
||||
>T : T
|
||||
>Banana : Banana
|
||||
|
||||
a: T;
|
||||
>a : T
|
||||
>T : T
|
||||
|
||||
render() {
|
||||
>render : () => void
|
||||
|
||||
if (this.a) {
|
||||
>this.a : T
|
||||
>this : this
|
||||
>a : T
|
||||
|
||||
this.a.color;
|
||||
>this.a.color : "yellow"
|
||||
>this.a : Banana
|
||||
>this : this
|
||||
>a : Banana
|
||||
>color : "yellow"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BigBanana extends Banana {
|
||||
>BigBanana : BigBanana
|
||||
>Banana : Banana
|
||||
}
|
||||
|
||||
class BigMonkey extends Monkey<BigBanana> {
|
||||
>BigMonkey : BigMonkey
|
||||
>Monkey : Monkey<BigBanana>
|
||||
>BigBanana : BigBanana
|
||||
|
||||
render() {
|
||||
>render : () => void
|
||||
|
||||
if (this.a) {
|
||||
>this.a : BigBanana
|
||||
>this : this
|
||||
>a : BigBanana
|
||||
|
||||
this.a.color;
|
||||
>this.a.color : "yellow"
|
||||
>this.a : BigBanana
|
||||
>this : this
|
||||
>a : BigBanana
|
||||
>color : "yellow"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another repro
|
||||
|
||||
type Item = {
|
||||
>Item : Item
|
||||
|
||||
(): string;
|
||||
x: string;
|
||||
>x : string
|
||||
}
|
||||
|
||||
function f1<T extends Item | undefined>(obj: T) {
|
||||
>f1 : <T extends Item | undefined>(obj: T) => void
|
||||
>T : T
|
||||
>Item : Item
|
||||
>obj : T
|
||||
>T : T
|
||||
|
||||
if (obj) {
|
||||
>obj : T
|
||||
|
||||
obj.x;
|
||||
>obj.x : string
|
||||
>obj : Item
|
||||
>x : string
|
||||
|
||||
obj["x"];
|
||||
>obj["x"] : string
|
||||
>obj : Item
|
||||
>"x" : "x"
|
||||
|
||||
obj();
|
||||
>obj() : string
|
||||
>obj : Item
|
||||
}
|
||||
}
|
||||
|
||||
function f2<T extends Item | undefined>(obj: T | undefined) {
|
||||
>f2 : <T extends Item | undefined>(obj: T | undefined) => void
|
||||
>T : T
|
||||
>Item : Item
|
||||
>obj : T | undefined
|
||||
>T : T
|
||||
|
||||
if (obj) {
|
||||
>obj : T | undefined
|
||||
|
||||
obj.x;
|
||||
>obj.x : string
|
||||
>obj : Item
|
||||
>x : string
|
||||
|
||||
obj["x"];
|
||||
>obj["x"] : string
|
||||
>obj : Item
|
||||
>"x" : "x"
|
||||
|
||||
obj();
|
||||
>obj() : string
|
||||
>obj : Item
|
||||
}
|
||||
}
|
||||
|
||||
function f3<T extends Item | undefined>(obj: T | null) {
|
||||
>f3 : <T extends Item | undefined>(obj: T | null) => void
|
||||
>T : T
|
||||
>Item : Item
|
||||
>obj : T | null
|
||||
>T : T
|
||||
>null : null
|
||||
|
||||
if (obj) {
|
||||
>obj : T | null
|
||||
|
||||
obj.x;
|
||||
>obj.x : string
|
||||
>obj : Item
|
||||
>x : string
|
||||
|
||||
obj["x"];
|
||||
>obj["x"] : string
|
||||
>obj : Item
|
||||
>"x" : "x"
|
||||
|
||||
obj();
|
||||
>obj() : string
|
||||
>obj : Item
|
||||
}
|
||||
}
|
||||
|
||||
function f4<T extends string[] | undefined>(obj: T | undefined, x: number) {
|
||||
>f4 : <T extends string[] | undefined>(obj: T | undefined, x: number) => void
|
||||
>T : T
|
||||
>obj : T | undefined
|
||||
>T : T
|
||||
>x : number
|
||||
|
||||
if (obj) {
|
||||
>obj : T | undefined
|
||||
|
||||
obj[x].length;
|
||||
>obj[x].length : number
|
||||
>obj[x] : string
|
||||
>obj : string[]
|
||||
>x : number
|
||||
>length : number
|
||||
}
|
||||
}
|
||||
|
||||
function f5<T, K extends keyof T>(obj: T | undefined, key: K) {
|
||||
>f5 : <T, K extends keyof T>(obj: T | undefined, key: K) => void
|
||||
>T : T
|
||||
>K : K
|
||||
>T : T
|
||||
>obj : T | undefined
|
||||
>T : T
|
||||
>key : K
|
||||
>K : K
|
||||
|
||||
if (obj) {
|
||||
>obj : T | undefined
|
||||
|
||||
obj[key];
|
||||
>obj[key] : T[K]
|
||||
>obj : T
|
||||
>key : K
|
||||
}
|
||||
}
|
||||
|
||||
83
tests/cases/compiler/typeVariableTypeGuards.ts
Normal file
83
tests/cases/compiler/typeVariableTypeGuards.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
// @strict: true
|
||||
|
||||
// Repro from #14091
|
||||
|
||||
interface Foo {
|
||||
foo(): void
|
||||
}
|
||||
|
||||
class A<P extends Partial<Foo>> {
|
||||
props: Readonly<P>
|
||||
doSomething() {
|
||||
this.props.foo && this.props.foo()
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #14415
|
||||
|
||||
interface Banana {
|
||||
color: 'yellow';
|
||||
}
|
||||
|
||||
class Monkey<T extends Banana | undefined> {
|
||||
a: T;
|
||||
render() {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface BigBanana extends Banana {
|
||||
}
|
||||
|
||||
class BigMonkey extends Monkey<BigBanana> {
|
||||
render() {
|
||||
if (this.a) {
|
||||
this.a.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another repro
|
||||
|
||||
type Item = {
|
||||
(): string;
|
||||
x: string;
|
||||
}
|
||||
|
||||
function f1<T extends Item | undefined>(obj: T) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f2<T extends Item | undefined>(obj: T | undefined) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f3<T extends Item | undefined>(obj: T | null) {
|
||||
if (obj) {
|
||||
obj.x;
|
||||
obj["x"];
|
||||
obj();
|
||||
}
|
||||
}
|
||||
|
||||
function f4<T extends string[] | undefined>(obj: T | undefined, x: number) {
|
||||
if (obj) {
|
||||
obj[x].length;
|
||||
}
|
||||
}
|
||||
|
||||
function f5<T, K extends keyof T>(obj: T | undefined, key: K) {
|
||||
if (obj) {
|
||||
obj[key];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user