mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Infer type predicates from function bodies using control flow analysis (#57465)
Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
This commit is contained in:
parent
60cf79127a
commit
e5bf594753
@ -1094,7 +1094,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
|
||||
inAssignmentPattern = saveInAssignmentPattern;
|
||||
return;
|
||||
}
|
||||
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && !options.allowUnreachableCode) {
|
||||
if (node.kind >= SyntaxKind.FirstStatement && node.kind <= SyntaxKind.LastStatement && (!options.allowUnreachableCode || node.kind === SyntaxKind.ReturnStatement)) {
|
||||
(node as HasFlowNode).flowNode = currentFlow;
|
||||
}
|
||||
switch (node.kind) {
|
||||
|
||||
@ -6456,6 +6456,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
function createNodeBuilder() {
|
||||
return {
|
||||
typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => typeToTypeNodeHelper(type, context)),
|
||||
typePredicateToTypePredicateNode: (typePredicate: TypePredicate, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => typePredicateToTypePredicateNodeHelper(typePredicate, context)),
|
||||
indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => indexInfoToIndexSignatureDeclarationHelper(indexInfo, context, /*typeNode*/ undefined)),
|
||||
signatureToSignatureDeclaration: (signature: Signature, kind: SignatureDeclaration["kind"], enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => signatureToSignatureDeclarationHelper(signature, kind, context)),
|
||||
symbolToEntityName: (symbol: Symbol, meaning: SymbolFlags, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => withContext(enclosingDeclaration, flags, tracker, context => symbolToName(symbol, context, meaning, /*expectsIdentifier*/ false)),
|
||||
@ -7704,14 +7705,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let returnTypeNode: TypeNode | undefined;
|
||||
const typePredicate = getTypePredicateOfSignature(signature);
|
||||
if (typePredicate) {
|
||||
const assertsModifier = typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ?
|
||||
factory.createToken(SyntaxKind.AssertsKeyword) :
|
||||
undefined;
|
||||
const parameterName = typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ?
|
||||
setEmitFlags(factory.createIdentifier(typePredicate.parameterName), EmitFlags.NoAsciiEscaping) :
|
||||
factory.createThisTypeNode();
|
||||
const typeNode = typePredicate.type && typeToTypeNodeHelper(typePredicate.type, context);
|
||||
returnTypeNode = factory.createTypePredicateNode(assertsModifier, parameterName, typeNode);
|
||||
returnTypeNode = typePredicateToTypePredicateNodeHelper(typePredicate, context);
|
||||
}
|
||||
else {
|
||||
const returnType = getReturnTypeOfSignature(signature);
|
||||
@ -7790,6 +7784,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return typeParameterToDeclarationWithConstraint(type, context, constraintNode);
|
||||
}
|
||||
|
||||
function typePredicateToTypePredicateNodeHelper(typePredicate: TypePredicate, context: NodeBuilderContext): TypePredicateNode {
|
||||
const assertsModifier = typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ?
|
||||
factory.createToken(SyntaxKind.AssertsKeyword) :
|
||||
undefined;
|
||||
const parameterName = typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ?
|
||||
setEmitFlags(factory.createIdentifier(typePredicate.parameterName), EmitFlags.NoAsciiEscaping) :
|
||||
factory.createThisTypeNode();
|
||||
const typeNode = typePredicate.type && typeToTypeNodeHelper(typePredicate.type, context);
|
||||
return factory.createTypePredicateNode(assertsModifier, parameterName, typeNode);
|
||||
}
|
||||
|
||||
function getEffectiveParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration | JSDocParameterTag | undefined {
|
||||
const parameterDeclaration: ParameterDeclaration | JSDocParameterTag | undefined = getDeclarationOfKind<ParameterDeclaration>(parameterSymbol, SyntaxKind.Parameter);
|
||||
if (parameterDeclaration) {
|
||||
@ -10309,11 +10314,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return writer ? typePredicateToStringWorker(writer).getText() : usingSingleLineStringWriter(typePredicateToStringWorker);
|
||||
|
||||
function typePredicateToStringWorker(writer: EmitTextWriter) {
|
||||
const predicate = factory.createTypePredicateNode(
|
||||
typePredicate.kind === TypePredicateKind.AssertsThis || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createToken(SyntaxKind.AssertsKeyword) : undefined,
|
||||
typePredicate.kind === TypePredicateKind.Identifier || typePredicate.kind === TypePredicateKind.AssertsIdentifier ? factory.createIdentifier(typePredicate.parameterName) : factory.createThisTypeNode(),
|
||||
typePredicate.type && nodeBuilder.typeToTypeNode(typePredicate.type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName)!, // TODO: GH#18217
|
||||
);
|
||||
const nodeBuilderFlags = toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName;
|
||||
const predicate = nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, nodeBuilderFlags)!; // TODO: GH#18217
|
||||
const printer = createPrinterWithRemoveComments();
|
||||
const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration);
|
||||
printer.writeNode(EmitHint.Unspecified, predicate, /*sourceFile*/ sourceFile, writer);
|
||||
@ -15476,9 +15478,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
jsdocPredicate = getTypePredicateOfSignature(jsdocSignature);
|
||||
}
|
||||
}
|
||||
signature.resolvedTypePredicate = type && isTypePredicateNode(type) ?
|
||||
createTypePredicateFromTypePredicateNode(type, signature) :
|
||||
jsdocPredicate || noTypePredicate;
|
||||
if (type || jsdocPredicate) {
|
||||
signature.resolvedTypePredicate = type && isTypePredicateNode(type) ?
|
||||
createTypePredicateFromTypePredicateNode(type, signature) :
|
||||
jsdocPredicate || noTypePredicate;
|
||||
}
|
||||
else if (signature.declaration && isFunctionLikeDeclaration(signature.declaration) && (!signature.resolvedReturnType || signature.resolvedReturnType.flags & TypeFlags.Boolean) && getParameterCount(signature) > 0) {
|
||||
const { declaration } = signature;
|
||||
signature.resolvedTypePredicate = noTypePredicate; // avoid infinite loop
|
||||
signature.resolvedTypePredicate = getTypePredicateFromBody(declaration) || noTypePredicate;
|
||||
}
|
||||
else {
|
||||
signature.resolvedTypePredicate = noTypePredicate;
|
||||
}
|
||||
}
|
||||
Debug.assert(!!signature.resolvedTypePredicate);
|
||||
}
|
||||
@ -37450,6 +37462,72 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
|
||||
function getTypePredicateFromBody(func: FunctionLikeDeclaration): TypePredicate | undefined {
|
||||
switch (func.kind) {
|
||||
case SyntaxKind.Constructor:
|
||||
case SyntaxKind.GetAccessor:
|
||||
case SyntaxKind.SetAccessor:
|
||||
return undefined;
|
||||
}
|
||||
const functionFlags = getFunctionFlags(func);
|
||||
if (functionFlags !== FunctionFlags.Normal) return undefined;
|
||||
|
||||
// Only attempt to infer a type predicate if there's exactly one return.
|
||||
let singleReturn: Expression | undefined;
|
||||
if (func.body && func.body.kind !== SyntaxKind.Block) {
|
||||
singleReturn = func.body; // arrow function
|
||||
}
|
||||
else {
|
||||
const bailedEarly = forEachReturnStatement(func.body as Block, returnStatement => {
|
||||
if (singleReturn || !returnStatement.expression) return true;
|
||||
singleReturn = returnStatement.expression;
|
||||
});
|
||||
if (bailedEarly || !singleReturn || functionHasImplicitReturn(func)) return undefined;
|
||||
}
|
||||
return checkIfExpressionRefinesAnyParameter(func, singleReturn);
|
||||
}
|
||||
|
||||
function checkIfExpressionRefinesAnyParameter(func: FunctionLikeDeclaration, expr: Expression): TypePredicate | undefined {
|
||||
expr = skipParentheses(expr, /*excludeJSDocTypeAssertions*/ true);
|
||||
const returnType = checkExpressionCached(expr);
|
||||
if (!(returnType.flags & TypeFlags.Boolean)) return undefined;
|
||||
|
||||
return forEach(func.parameters, (param, i) => {
|
||||
const initType = getTypeOfSymbol(param.symbol);
|
||||
if (!initType || initType.flags & TypeFlags.Boolean || !isIdentifier(param.name) || isSymbolAssigned(param.symbol) || isRestParameter(param)) {
|
||||
// Refining "x: boolean" to "x is true" or "x is false" isn't useful.
|
||||
return;
|
||||
}
|
||||
const trueType = checkIfExpressionRefinesParameter(func, expr, param, initType);
|
||||
if (trueType) {
|
||||
return createTypePredicate(TypePredicateKind.Identifier, unescapeLeadingUnderscores(param.name.escapedText), i, trueType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkIfExpressionRefinesParameter(func: FunctionLikeDeclaration, expr: Expression, param: ParameterDeclaration, initType: Type): Type | undefined {
|
||||
const antecedent = (expr as Expression & { flowNode?: FlowNode; }).flowNode ||
|
||||
expr.parent.kind === SyntaxKind.ReturnStatement && (expr.parent as ReturnStatement).flowNode ||
|
||||
{ flags: FlowFlags.Start };
|
||||
const trueCondition: FlowCondition = {
|
||||
flags: FlowFlags.TrueCondition,
|
||||
node: expr,
|
||||
antecedent,
|
||||
};
|
||||
|
||||
const trueType = getFlowTypeOfReference(param.name, initType, initType, func, trueCondition);
|
||||
if (trueType === initType) return undefined;
|
||||
|
||||
// "x is T" means that x is T if and only if it returns true. If it returns false then x is not T.
|
||||
// This means that if the function is called with an argument of type trueType, there can't be anything left in the `else` branch. It must reduce to `never`.
|
||||
const falseCondition: FlowCondition = {
|
||||
...trueCondition,
|
||||
flags: FlowFlags.FalseCondition,
|
||||
};
|
||||
const falseSubtype = getFlowTypeOfReference(param.name, trueType, trueType, func, falseCondition);
|
||||
return falseSubtype.flags & TypeFlags.Never ? trueType : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* TypeScript Specification 1.0 (6.3) - July 2014
|
||||
* An explicitly typed function whose return type isn't the Void type,
|
||||
@ -48511,6 +48589,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return factory.createToken(SyntaxKind.AnyKeyword) as KeywordTypeNode;
|
||||
}
|
||||
const signature = getSignatureFromDeclaration(signatureDeclaration);
|
||||
const typePredicate = getTypePredicateOfSignature(signature);
|
||||
if (typePredicate) {
|
||||
// Inferred type predicates
|
||||
return nodeBuilder.typePredicateToTypePredicateNode(typePredicate, enclosingDeclaration, flags | NodeBuilderFlags.MultilineObjectLiterals, tracker);
|
||||
}
|
||||
return nodeBuilder.typeToTypeNode(getReturnTypeOfSignature(signature), enclosingDeclaration, flags | NodeBuilderFlags.MultilineObjectLiterals, tracker);
|
||||
}
|
||||
|
||||
|
||||
40
tests/baselines/reference/circularConstructorWithReturn.js
Normal file
40
tests/baselines/reference/circularConstructorWithReturn.js
Normal file
@ -0,0 +1,40 @@
|
||||
//// [tests/cases/compiler/circularConstructorWithReturn.ts] ////
|
||||
|
||||
//// [circularConstructorWithReturn.ts]
|
||||
// This should not be a circularity error. See
|
||||
// https://github.com/microsoft/TypeScript/pull/57465#issuecomment-1960271216
|
||||
export type Client = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never
|
||||
|
||||
export function getPrismaClient(options?: any) {
|
||||
class PrismaClient {
|
||||
self: Client;
|
||||
constructor(options?: any) {
|
||||
return (this.self = applyModelsAndClientExtensions(this));
|
||||
}
|
||||
}
|
||||
|
||||
return PrismaClient
|
||||
}
|
||||
|
||||
export function applyModelsAndClientExtensions(client: Client) {
|
||||
return client;
|
||||
}
|
||||
|
||||
|
||||
//// [circularConstructorWithReturn.js]
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getPrismaClient = getPrismaClient;
|
||||
exports.applyModelsAndClientExtensions = applyModelsAndClientExtensions;
|
||||
function getPrismaClient(options) {
|
||||
var PrismaClient = /** @class */ (function () {
|
||||
function PrismaClient(options) {
|
||||
return (this.self = applyModelsAndClientExtensions(this));
|
||||
}
|
||||
return PrismaClient;
|
||||
}());
|
||||
return PrismaClient;
|
||||
}
|
||||
function applyModelsAndClientExtensions(client) {
|
||||
return client;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
//// [tests/cases/compiler/circularConstructorWithReturn.ts] ////
|
||||
|
||||
=== circularConstructorWithReturn.ts ===
|
||||
// This should not be a circularity error. See
|
||||
// https://github.com/microsoft/TypeScript/pull/57465#issuecomment-1960271216
|
||||
export type Client = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never
|
||||
>Client : Symbol(Client, Decl(circularConstructorWithReturn.ts, 0, 0))
|
||||
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
|
||||
>getPrismaClient : Symbol(getPrismaClient, Decl(circularConstructorWithReturn.ts, 2, 93))
|
||||
>T : Symbol(T, Decl(circularConstructorWithReturn.ts, 2, 79))
|
||||
>T : Symbol(T, Decl(circularConstructorWithReturn.ts, 2, 79))
|
||||
|
||||
export function getPrismaClient(options?: any) {
|
||||
>getPrismaClient : Symbol(getPrismaClient, Decl(circularConstructorWithReturn.ts, 2, 93))
|
||||
>options : Symbol(options, Decl(circularConstructorWithReturn.ts, 4, 32))
|
||||
|
||||
class PrismaClient {
|
||||
>PrismaClient : Symbol(PrismaClient, Decl(circularConstructorWithReturn.ts, 4, 48))
|
||||
|
||||
self: Client;
|
||||
>self : Symbol(PrismaClient.self, Decl(circularConstructorWithReturn.ts, 5, 22))
|
||||
>Client : Symbol(Client, Decl(circularConstructorWithReturn.ts, 0, 0))
|
||||
|
||||
constructor(options?: any) {
|
||||
>options : Symbol(options, Decl(circularConstructorWithReturn.ts, 7, 16))
|
||||
|
||||
return (this.self = applyModelsAndClientExtensions(this));
|
||||
>this.self : Symbol(PrismaClient.self, Decl(circularConstructorWithReturn.ts, 5, 22))
|
||||
>this : Symbol(PrismaClient, Decl(circularConstructorWithReturn.ts, 4, 48))
|
||||
>self : Symbol(PrismaClient.self, Decl(circularConstructorWithReturn.ts, 5, 22))
|
||||
>applyModelsAndClientExtensions : Symbol(applyModelsAndClientExtensions, Decl(circularConstructorWithReturn.ts, 13, 1))
|
||||
>this : Symbol(PrismaClient, Decl(circularConstructorWithReturn.ts, 4, 48))
|
||||
}
|
||||
}
|
||||
|
||||
return PrismaClient
|
||||
>PrismaClient : Symbol(PrismaClient, Decl(circularConstructorWithReturn.ts, 4, 48))
|
||||
}
|
||||
|
||||
export function applyModelsAndClientExtensions(client: Client) {
|
||||
>applyModelsAndClientExtensions : Symbol(applyModelsAndClientExtensions, Decl(circularConstructorWithReturn.ts, 13, 1))
|
||||
>client : Symbol(client, Decl(circularConstructorWithReturn.ts, 15, 47))
|
||||
>Client : Symbol(Client, Decl(circularConstructorWithReturn.ts, 0, 0))
|
||||
|
||||
return client;
|
||||
>client : Symbol(client, Decl(circularConstructorWithReturn.ts, 15, 47))
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
//// [tests/cases/compiler/circularConstructorWithReturn.ts] ////
|
||||
|
||||
=== circularConstructorWithReturn.ts ===
|
||||
// This should not be a circularity error. See
|
||||
// https://github.com/microsoft/TypeScript/pull/57465#issuecomment-1960271216
|
||||
export type Client = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never
|
||||
>Client : PrismaClient
|
||||
>getPrismaClient : (options?: any) => typeof PrismaClient
|
||||
|
||||
export function getPrismaClient(options?: any) {
|
||||
>getPrismaClient : (options?: any) => typeof PrismaClient
|
||||
>options : any
|
||||
|
||||
class PrismaClient {
|
||||
>PrismaClient : PrismaClient
|
||||
|
||||
self: Client;
|
||||
>self : PrismaClient
|
||||
|
||||
constructor(options?: any) {
|
||||
>options : any
|
||||
|
||||
return (this.self = applyModelsAndClientExtensions(this));
|
||||
>(this.self = applyModelsAndClientExtensions(this)) : PrismaClient
|
||||
>this.self = applyModelsAndClientExtensions(this) : PrismaClient
|
||||
>this.self : PrismaClient
|
||||
>this : this
|
||||
>self : PrismaClient
|
||||
>applyModelsAndClientExtensions(this) : PrismaClient
|
||||
>applyModelsAndClientExtensions : (client: PrismaClient) => PrismaClient
|
||||
>this : this
|
||||
}
|
||||
}
|
||||
|
||||
return PrismaClient
|
||||
>PrismaClient : typeof PrismaClient
|
||||
}
|
||||
|
||||
export function applyModelsAndClientExtensions(client: Client) {
|
||||
>applyModelsAndClientExtensions : (client: Client) => PrismaClient
|
||||
>client : PrismaClient
|
||||
|
||||
return client;
|
||||
>client : PrismaClient
|
||||
}
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
=== findLast.ts ===
|
||||
const itemNumber: number | undefined = [0].findLast((item) => item === 0);
|
||||
>itemNumber : number
|
||||
>[0].findLast((item) => item === 0) : number
|
||||
>[0].findLast((item) => item === 0) : 0
|
||||
>[0].findLast : { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number; }
|
||||
>[0] : number[]
|
||||
>0 : 0
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -16,120 +16,120 @@ const itemNumber: number | undefined = [0].findLast((item) => item === 0);
|
||||
|
||||
const itemString: string | undefined = ["string"].findLast((item) => item === "string");
|
||||
>itemString : string
|
||||
>["string"].findLast((item) => item === "string") : string
|
||||
>["string"].findLast((item) => item === "string") : "string"
|
||||
>["string"].findLast : { <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string; }
|
||||
>["string"] : string[]
|
||||
>"string" : "string"
|
||||
>findLast : { <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string; }
|
||||
>(item) => item === "string" : (item: string) => boolean
|
||||
>(item) => item === "string" : (item: string) => item is "string"
|
||||
>item : string
|
||||
>item === "string" : boolean
|
||||
>item : string
|
||||
>"string" : "string"
|
||||
|
||||
new Int8Array().findLast((item) => item === 0);
|
||||
>new Int8Array().findLast((item) => item === 0) : number
|
||||
>new Int8Array().findLast((item) => item === 0) : 0
|
||||
>new Int8Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Int8Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int8Array) => unknown, thisArg?: any): number; }
|
||||
>new Int8Array() : Int8Array
|
||||
>Int8Array : Int8ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Int8Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int8Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Uint8Array().findLast((item) => item === 0);
|
||||
>new Uint8Array().findLast((item) => item === 0) : number
|
||||
>new Uint8Array().findLast((item) => item === 0) : 0
|
||||
>new Uint8Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint8Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint8Array) => unknown, thisArg?: any): number; }
|
||||
>new Uint8Array() : Uint8Array
|
||||
>Uint8Array : Uint8ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint8Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint8Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Uint8ClampedArray().findLast((item) => item === 0);
|
||||
>new Uint8ClampedArray().findLast((item) => item === 0) : number
|
||||
>new Uint8ClampedArray().findLast((item) => item === 0) : 0
|
||||
>new Uint8ClampedArray().findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint8ClampedArray) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint8ClampedArray) => unknown, thisArg?: any): number; }
|
||||
>new Uint8ClampedArray() : Uint8ClampedArray
|
||||
>Uint8ClampedArray : Uint8ClampedArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint8ClampedArray) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint8ClampedArray) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Int16Array().findLast((item) => item === 0);
|
||||
>new Int16Array().findLast((item) => item === 0) : number
|
||||
>new Int16Array().findLast((item) => item === 0) : 0
|
||||
>new Int16Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Int16Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int16Array) => unknown, thisArg?: any): number; }
|
||||
>new Int16Array() : Int16Array
|
||||
>Int16Array : Int16ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Int16Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int16Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Uint16Array().findLast((item) => item === 0);
|
||||
>new Uint16Array().findLast((item) => item === 0) : number
|
||||
>new Uint16Array().findLast((item) => item === 0) : 0
|
||||
>new Uint16Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint16Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint16Array) => unknown, thisArg?: any): number; }
|
||||
>new Uint16Array() : Uint16Array
|
||||
>Uint16Array : Uint16ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint16Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint16Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Int32Array().findLast((item) => item === 0);
|
||||
>new Int32Array().findLast((item) => item === 0) : number
|
||||
>new Int32Array().findLast((item) => item === 0) : 0
|
||||
>new Int32Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Int32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int32Array) => unknown, thisArg?: any): number; }
|
||||
>new Int32Array() : Int32Array
|
||||
>Int32Array : Int32ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Int32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Int32Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Uint32Array().findLast((item) => item === 0);
|
||||
>new Uint32Array().findLast((item) => item === 0) : number
|
||||
>new Uint32Array().findLast((item) => item === 0) : 0
|
||||
>new Uint32Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint32Array) => unknown, thisArg?: any): number; }
|
||||
>new Uint32Array() : Uint32Array
|
||||
>Uint32Array : Uint32ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Uint32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Uint32Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Float32Array().findLast((item) => item === 0);
|
||||
>new Float32Array().findLast((item) => item === 0) : number
|
||||
>new Float32Array().findLast((item) => item === 0) : 0
|
||||
>new Float32Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Float32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Float32Array) => unknown, thisArg?: any): number; }
|
||||
>new Float32Array() : Float32Array
|
||||
>Float32Array : Float32ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Float32Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Float32Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
>0 : 0
|
||||
|
||||
new Float64Array().findLast((item) => item === 0);
|
||||
>new Float64Array().findLast((item) => item === 0) : number
|
||||
>new Float64Array().findLast((item) => item === 0) : 0
|
||||
>new Float64Array().findLast : { <S extends number>(predicate: (value: number, index: number, array: Float64Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Float64Array) => unknown, thisArg?: any): number; }
|
||||
>new Float64Array() : Float64Array
|
||||
>Float64Array : Float64ArrayConstructor
|
||||
>findLast : { <S extends number>(predicate: (value: number, index: number, array: Float64Array) => value is S, thisArg?: any): S; (predicate: (value: number, index: number, array: Float64Array) => unknown, thisArg?: any): number; }
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -170,7 +170,7 @@ const indexNumber: number = [0].findLastIndex((item) => item === 0);
|
||||
>[0] : number[]
|
||||
>0 : 0
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -183,7 +183,7 @@ const indexString: number = ["string"].findLastIndex((item) => item === "string"
|
||||
>["string"] : string[]
|
||||
>"string" : "string"
|
||||
>findLastIndex : (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any) => number
|
||||
>(item) => item === "string" : (item: string) => boolean
|
||||
>(item) => item === "string" : (item: string) => item is "string"
|
||||
>item : string
|
||||
>item === "string" : boolean
|
||||
>item : string
|
||||
@ -195,7 +195,7 @@ new Int8Array().findLastIndex((item) => item === 0);
|
||||
>new Int8Array() : Int8Array
|
||||
>Int8Array : Int8ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Int8Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -207,7 +207,7 @@ new Uint8Array().findLastIndex((item) => item === 0);
|
||||
>new Uint8Array() : Uint8Array
|
||||
>Uint8Array : Uint8ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Uint8Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -219,7 +219,7 @@ new Uint8ClampedArray().findLastIndex((item) => item === 0);
|
||||
>new Uint8ClampedArray() : Uint8ClampedArray
|
||||
>Uint8ClampedArray : Uint8ClampedArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Uint8ClampedArray) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -231,7 +231,7 @@ new Int16Array().findLastIndex((item) => item === 0);
|
||||
>new Int16Array() : Int16Array
|
||||
>Int16Array : Int16ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Int16Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -243,7 +243,7 @@ new Uint16Array().findLastIndex((item) => item === 0);
|
||||
>new Uint16Array() : Uint16Array
|
||||
>Uint16Array : Uint16ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Uint16Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -255,7 +255,7 @@ new Int32Array().findLastIndex((item) => item === 0);
|
||||
>new Int32Array() : Int32Array
|
||||
>Int32Array : Int32ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Int32Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -267,7 +267,7 @@ new Uint32Array().findLastIndex((item) => item === 0);
|
||||
>new Uint32Array() : Uint32Array
|
||||
>Uint32Array : Uint32ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Uint32Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -279,7 +279,7 @@ new Float32Array().findLastIndex((item) => item === 0);
|
||||
>new Float32Array() : Float32Array
|
||||
>Float32Array : Float32ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Float32Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
@ -291,7 +291,7 @@ new Float64Array().findLastIndex((item) => item === 0);
|
||||
>new Float64Array() : Float64Array
|
||||
>Float64Array : Float64ArrayConstructor
|
||||
>findLastIndex : (predicate: (value: number, index: number, array: Float64Array) => unknown, thisArg?: any) => number
|
||||
>(item) => item === 0 : (item: number) => boolean
|
||||
>(item) => item === 0 : (item: number) => item is 0
|
||||
>item : number
|
||||
>item === 0 : boolean
|
||||
>item : number
|
||||
|
||||
@ -22,7 +22,7 @@ declare var dec: any;
|
||||
>this : this
|
||||
|
||||
g(u) { return #x in u; }
|
||||
>g : (u: any) => boolean
|
||||
>g : (u: any) => u is A
|
||||
>u : any
|
||||
>#x in u : boolean
|
||||
>#x : any
|
||||
|
||||
@ -79,7 +79,7 @@ function f1(x: unknown) {
|
||||
}
|
||||
|
||||
function f2<T>(x: T) {
|
||||
>f2 : <T>(x: T) => boolean
|
||||
>f2 : <T>(x: T) => x is T & Object & Record<"a", unknown>
|
||||
>x : T
|
||||
|
||||
return x && x instanceof Object && 'a' in x;
|
||||
@ -95,7 +95,7 @@ function f2<T>(x: T) {
|
||||
}
|
||||
|
||||
function f3(x: {}) {
|
||||
>f3 : (x: {}) => boolean
|
||||
>f3 : (x: {}) => x is Object & Record<"a", unknown>
|
||||
>x : {}
|
||||
|
||||
return x instanceof Object && 'a' in x;
|
||||
@ -109,7 +109,7 @@ function f3(x: {}) {
|
||||
}
|
||||
|
||||
function f4<T extends {}>(x: T) {
|
||||
>f4 : <T extends {}>(x: T) => boolean
|
||||
>f4 : <T extends {}>(x: T) => x is T & Object & Record<"a", unknown>
|
||||
>x : T
|
||||
|
||||
return x instanceof Object && 'a' in x;
|
||||
@ -123,7 +123,7 @@ function f4<T extends {}>(x: T) {
|
||||
}
|
||||
|
||||
function f5<T>(x: T & {}) {
|
||||
>f5 : <T>(x: T & {}) => boolean
|
||||
>f5 : <T>(x: T & {}) => x is T & Object & Record<"a", unknown>
|
||||
>x : T & {}
|
||||
|
||||
return x instanceof Object && 'a' in x;
|
||||
@ -137,7 +137,7 @@ function f5<T>(x: T & {}) {
|
||||
}
|
||||
|
||||
function f6<T extends {}>(x: T & {}) {
|
||||
>f6 : <T extends {}>(x: T & {}) => boolean
|
||||
>f6 : <T extends {}>(x: T & {}) => x is T & Object & Record<"a", unknown>
|
||||
>x : T
|
||||
|
||||
return x instanceof Object && 'a' in x;
|
||||
@ -151,7 +151,7 @@ function f6<T extends {}>(x: T & {}) {
|
||||
}
|
||||
|
||||
function f7<T extends object>(x: T & {}) {
|
||||
>f7 : <T extends object>(x: T & {}) => boolean
|
||||
>f7 : <T extends object>(x: T & {}) => x is T & Record<"a", unknown>
|
||||
>x : T
|
||||
|
||||
return x instanceof Object && 'a' in x;
|
||||
|
||||
318
tests/baselines/reference/inferTypePredicates.errors.txt
Normal file
318
tests/baselines/reference/inferTypePredicates.errors.txt
Normal file
@ -0,0 +1,318 @@
|
||||
inferTypePredicates.ts(4,7): error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
Type 'number | null' is not assignable to type 'number'.
|
||||
Type 'null' is not assignable to type 'number'.
|
||||
inferTypePredicates.ts(7,7): error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
inferTypePredicates.ts(14,7): error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
inferTypePredicates.ts(52,17): error TS18048: 'arr' is possibly 'undefined'.
|
||||
inferTypePredicates.ts(54,28): error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
|
||||
Type 'undefined' is not assignable to type 'string'.
|
||||
inferTypePredicates.ts(65,28): error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
|
||||
Type 'undefined' is not assignable to type 'string'.
|
||||
inferTypePredicates.ts(90,8): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
|
||||
inferTypePredicates.ts(113,7): error TS2322: Type 'string | number' is not assignable to type 'string'.
|
||||
Type 'number' is not assignable to type 'string'.
|
||||
inferTypePredicates.ts(115,7): error TS2322: Type 'string | number' is not assignable to type 'number'.
|
||||
Type 'string' is not assignable to type 'number'.
|
||||
inferTypePredicates.ts(133,7): error TS2740: Type '{}' is missing the following properties from type 'Date': toDateString, toTimeString, toLocaleDateString, toLocaleTimeString, and 37 more.
|
||||
inferTypePredicates.ts(205,7): error TS2741: Property 'z' is missing in type 'C1' but required in type 'C2'.
|
||||
|
||||
|
||||
==== inferTypePredicates.ts (11 errors) ====
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
|
||||
const numsOrNull = [1, 2, 3, 4, null];
|
||||
const filteredNumsTruthy: number[] = numsOrNull.filter(x => !!x); // should error
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
!!! error TS2322: Type 'number | null' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'null' is not assignable to type 'number'.
|
||||
const filteredNumsNonNullish: number[] = numsOrNull.filter(x => x !== null); // should ok
|
||||
|
||||
const evenSquaresInline: number[] = // should error
|
||||
~~~~~~~~~~~~~~~~~
|
||||
!!! error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => !!x); // tests truthiness, not non-nullishness
|
||||
|
||||
const isTruthy = (x: number | null) => !!x;
|
||||
|
||||
const evenSquares: number[] = // should error
|
||||
~~~~~~~~~~~
|
||||
!!! error TS2322: Type '(number | null)[]' is not assignable to type 'number[]'.
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(isTruthy);
|
||||
|
||||
const evenSquaresNonNull: number[] = // should ok
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => x !== null);
|
||||
|
||||
function isNonNull(x: number | null) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x: number | null) {
|
||||
const ok = x !== null;
|
||||
return ok;
|
||||
}
|
||||
|
||||
function isNonNullGeneric<T>(x: T) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// Type guards can flow between functions
|
||||
const myGuard = (o: string | undefined): o is string => !!o;
|
||||
const mySecondGuard = (o: string | undefined) => myGuard(o);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1327449914
|
||||
// This doesn't work because the false condition prevents type guard inference.
|
||||
// Breaking up the filters does work.
|
||||
type MyObj = { data?: string };
|
||||
type MyArray = { list?: MyObj[] }[];
|
||||
const myArray: MyArray = [];
|
||||
|
||||
const result = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => arr && arr.length)
|
||||
.map((arr) => arr // should error
|
||||
~~~
|
||||
!!! error TS18048: 'arr' is possibly 'undefined'.
|
||||
.filter((obj) => obj && obj.data)
|
||||
.map(obj => JSON.parse(obj.data)) // should error
|
||||
~~~~~~~~
|
||||
!!! error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
|
||||
!!! error TS2345: Type 'undefined' is not assignable to type 'string'.
|
||||
);
|
||||
|
||||
const result2 = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => !!arr)
|
||||
.filter(arr => arr.length)
|
||||
.map((arr) => arr // should ok
|
||||
.filter((obj) => obj)
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(obj => !!obj.data)
|
||||
.map(obj => JSON.parse(obj.data))
|
||||
~~~~~~~~
|
||||
!!! error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
|
||||
!!! error TS2345: Type 'undefined' is not assignable to type 'string'.
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1183547889
|
||||
type Foo = {
|
||||
foo: string;
|
||||
}
|
||||
type Bar = Foo & {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
const list: (Foo | Bar)[] = [];
|
||||
const resultBars: Bar[] = list.filter((value) => 'bar' in value); // should ok
|
||||
|
||||
function isBarNonNull(x: Foo | Bar | null) {
|
||||
return ('bar' in x!);
|
||||
}
|
||||
const fooOrBar = list[0];
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
const t: Bar = fooOrBar; // should ok
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
const a = [1, "foo", 2, "bar"].filter(x => typeof x === "string");
|
||||
a.push(10);
|
||||
~~
|
||||
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
|
||||
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x: number|string): x is number {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x: string | number) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let strOrNum: string | number;
|
||||
if (isString(strOrNum)) {
|
||||
let t: string = strOrNum; // should ok
|
||||
} else {
|
||||
let t: number = strOrNum; // should ok
|
||||
}
|
||||
|
||||
function flakyIsString(x: string | number) {
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
let t: string = strOrNum; // should error
|
||||
~
|
||||
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
|
||||
!!! error TS2322: Type 'number' is not assignable to type 'string'.
|
||||
} else {
|
||||
let t: number = strOrNum; // should error
|
||||
~
|
||||
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
|
||||
!!! error TS2322: Type 'string' is not assignable to type 'number'.
|
||||
}
|
||||
|
||||
function isDate(x: object) {
|
||||
return x instanceof Date;
|
||||
}
|
||||
function flakyIsDate(x: object) {
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
}
|
||||
|
||||
declare let maybeDate: object;
|
||||
if (isDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should ok
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should error
|
||||
~
|
||||
!!! error TS2740: Type '{}' is missing the following properties from type 'Date': toDateString, toTimeString, toLocaleDateString, toLocaleTimeString, and 37 more.
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x: string | number) {
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
return typeof x === 'string';
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x: string | number) {
|
||||
[x] = [Math.random() < 0.5 ? "string" : 123];
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x: string|number, y: string|number) {
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
}
|
||||
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x: string|number) {
|
||||
if (typeof x === 'string') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function guardsOneButNotOthers(a: string|number, b: string|number, c: string|number) {
|
||||
return typeof b === 'string';
|
||||
}
|
||||
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x: number | string) {
|
||||
return typeof __x === 'string';
|
||||
}
|
||||
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
const booleanIdentity = (x: boolean) => x;
|
||||
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
const numOrBoolean = (x: number | boolean) => typeof x === 'number' || x;
|
||||
|
||||
// inferred guards in methods
|
||||
interface NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
}
|
||||
class Inferrer implements NumberInferrer {
|
||||
isNumber(x: number | string) { // should ok
|
||||
return typeof x === 'number';
|
||||
}
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
const inf = new Inferrer();
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
let t: number = numOrStr; // should ok
|
||||
} else {
|
||||
let t: string = numOrStr; // should ok
|
||||
}
|
||||
|
||||
// Type predicates are not inferred on "this"
|
||||
class C1 {
|
||||
isC2() {
|
||||
return this instanceof C2;
|
||||
}
|
||||
}
|
||||
class C2 extends C1 {
|
||||
z = 0;
|
||||
}
|
||||
declare let c: C1;
|
||||
if (c.isC2()) {
|
||||
let c2: C2 = c; // should error
|
||||
~~
|
||||
!!! error TS2741: Property 'z' is missing in type 'C1' but required in type 'C2'.
|
||||
!!! related TS2728 inferTypePredicates.ts:201:3: 'z' is declared here.
|
||||
}
|
||||
|
||||
function doNotRefineDestructuredParam({x, y}: {x: number | null, y: number}) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x: unknown) {
|
||||
return typeof x === "string" && x.length < 10;
|
||||
}
|
||||
|
||||
declare let str: string;
|
||||
if (isShortString(str)) {
|
||||
str.charAt(0); // should ok
|
||||
} else {
|
||||
str.charAt(0); // should ok
|
||||
}
|
||||
|
||||
function isStringFromUnknown(x: unknown) {
|
||||
return typeof x === "string";
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
str.charAt(0); // should OK
|
||||
} else {
|
||||
let t: never = str; // should OK
|
||||
}
|
||||
|
||||
// infer a union type
|
||||
function isNumOrStr(x: unknown) {
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
}
|
||||
declare let unk: unknown;
|
||||
if (isNumOrStr(unk)) {
|
||||
let t: number | string = unk; // should ok
|
||||
}
|
||||
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x: string | number | Date) {
|
||||
if (x instanceof Date) {
|
||||
throw new Error();
|
||||
}
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let snd: string | number | Date;
|
||||
if (assertAndPredicate(snd)) {
|
||||
let t: string = snd; // should error
|
||||
}
|
||||
|
||||
function isNumberWithThis(this: Date, x: number | string) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
function narrowFromAny(x: any) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
const noInferenceFromRest = (...f: ["a" | "b"]) => f[0] === "a";
|
||||
const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
|
||||
|
||||
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
607
tests/baselines/reference/inferTypePredicates.js
Normal file
607
tests/baselines/reference/inferTypePredicates.js
Normal file
@ -0,0 +1,607 @@
|
||||
//// [tests/cases/compiler/inferTypePredicates.ts] ////
|
||||
|
||||
//// [inferTypePredicates.ts]
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
|
||||
const numsOrNull = [1, 2, 3, 4, null];
|
||||
const filteredNumsTruthy: number[] = numsOrNull.filter(x => !!x); // should error
|
||||
const filteredNumsNonNullish: number[] = numsOrNull.filter(x => x !== null); // should ok
|
||||
|
||||
const evenSquaresInline: number[] = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => !!x); // tests truthiness, not non-nullishness
|
||||
|
||||
const isTruthy = (x: number | null) => !!x;
|
||||
|
||||
const evenSquares: number[] = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(isTruthy);
|
||||
|
||||
const evenSquaresNonNull: number[] = // should ok
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => x !== null);
|
||||
|
||||
function isNonNull(x: number | null) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x: number | null) {
|
||||
const ok = x !== null;
|
||||
return ok;
|
||||
}
|
||||
|
||||
function isNonNullGeneric<T>(x: T) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// Type guards can flow between functions
|
||||
const myGuard = (o: string | undefined): o is string => !!o;
|
||||
const mySecondGuard = (o: string | undefined) => myGuard(o);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1327449914
|
||||
// This doesn't work because the false condition prevents type guard inference.
|
||||
// Breaking up the filters does work.
|
||||
type MyObj = { data?: string };
|
||||
type MyArray = { list?: MyObj[] }[];
|
||||
const myArray: MyArray = [];
|
||||
|
||||
const result = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => arr && arr.length)
|
||||
.map((arr) => arr // should error
|
||||
.filter((obj) => obj && obj.data)
|
||||
.map(obj => JSON.parse(obj.data)) // should error
|
||||
);
|
||||
|
||||
const result2 = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => !!arr)
|
||||
.filter(arr => arr.length)
|
||||
.map((arr) => arr // should ok
|
||||
.filter((obj) => obj)
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(obj => !!obj.data)
|
||||
.map(obj => JSON.parse(obj.data))
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1183547889
|
||||
type Foo = {
|
||||
foo: string;
|
||||
}
|
||||
type Bar = Foo & {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
const list: (Foo | Bar)[] = [];
|
||||
const resultBars: Bar[] = list.filter((value) => 'bar' in value); // should ok
|
||||
|
||||
function isBarNonNull(x: Foo | Bar | null) {
|
||||
return ('bar' in x!);
|
||||
}
|
||||
const fooOrBar = list[0];
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
const t: Bar = fooOrBar; // should ok
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
const a = [1, "foo", 2, "bar"].filter(x => typeof x === "string");
|
||||
a.push(10);
|
||||
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x: number|string): x is number {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x: string | number) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let strOrNum: string | number;
|
||||
if (isString(strOrNum)) {
|
||||
let t: string = strOrNum; // should ok
|
||||
} else {
|
||||
let t: number = strOrNum; // should ok
|
||||
}
|
||||
|
||||
function flakyIsString(x: string | number) {
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
let t: string = strOrNum; // should error
|
||||
} else {
|
||||
let t: number = strOrNum; // should error
|
||||
}
|
||||
|
||||
function isDate(x: object) {
|
||||
return x instanceof Date;
|
||||
}
|
||||
function flakyIsDate(x: object) {
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
}
|
||||
|
||||
declare let maybeDate: object;
|
||||
if (isDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should ok
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should error
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x: string | number) {
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
return typeof x === 'string';
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x: string | number) {
|
||||
[x] = [Math.random() < 0.5 ? "string" : 123];
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x: string|number, y: string|number) {
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
}
|
||||
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x: string|number) {
|
||||
if (typeof x === 'string') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function guardsOneButNotOthers(a: string|number, b: string|number, c: string|number) {
|
||||
return typeof b === 'string';
|
||||
}
|
||||
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x: number | string) {
|
||||
return typeof __x === 'string';
|
||||
}
|
||||
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
const booleanIdentity = (x: boolean) => x;
|
||||
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
const numOrBoolean = (x: number | boolean) => typeof x === 'number' || x;
|
||||
|
||||
// inferred guards in methods
|
||||
interface NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
}
|
||||
class Inferrer implements NumberInferrer {
|
||||
isNumber(x: number | string) { // should ok
|
||||
return typeof x === 'number';
|
||||
}
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
const inf = new Inferrer();
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
let t: number = numOrStr; // should ok
|
||||
} else {
|
||||
let t: string = numOrStr; // should ok
|
||||
}
|
||||
|
||||
// Type predicates are not inferred on "this"
|
||||
class C1 {
|
||||
isC2() {
|
||||
return this instanceof C2;
|
||||
}
|
||||
}
|
||||
class C2 extends C1 {
|
||||
z = 0;
|
||||
}
|
||||
declare let c: C1;
|
||||
if (c.isC2()) {
|
||||
let c2: C2 = c; // should error
|
||||
}
|
||||
|
||||
function doNotRefineDestructuredParam({x, y}: {x: number | null, y: number}) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x: unknown) {
|
||||
return typeof x === "string" && x.length < 10;
|
||||
}
|
||||
|
||||
declare let str: string;
|
||||
if (isShortString(str)) {
|
||||
str.charAt(0); // should ok
|
||||
} else {
|
||||
str.charAt(0); // should ok
|
||||
}
|
||||
|
||||
function isStringFromUnknown(x: unknown) {
|
||||
return typeof x === "string";
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
str.charAt(0); // should OK
|
||||
} else {
|
||||
let t: never = str; // should OK
|
||||
}
|
||||
|
||||
// infer a union type
|
||||
function isNumOrStr(x: unknown) {
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
}
|
||||
declare let unk: unknown;
|
||||
if (isNumOrStr(unk)) {
|
||||
let t: number | string = unk; // should ok
|
||||
}
|
||||
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x: string | number | Date) {
|
||||
if (x instanceof Date) {
|
||||
throw new Error();
|
||||
}
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let snd: string | number | Date;
|
||||
if (assertAndPredicate(snd)) {
|
||||
let t: string = snd; // should error
|
||||
}
|
||||
|
||||
function isNumberWithThis(this: Date, x: number | string) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
function narrowFromAny(x: any) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
const noInferenceFromRest = (...f: ["a" | "b"]) => f[0] === "a";
|
||||
const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
|
||||
|
||||
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
|
||||
//// [inferTypePredicates.js]
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
if (typeof b !== "function" && b !== null)
|
||||
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var numsOrNull = [1, 2, 3, 4, null];
|
||||
var filteredNumsTruthy = numsOrNull.filter(function (x) { return !!x; }); // should error
|
||||
var filteredNumsNonNullish = numsOrNull.filter(function (x) { return x !== null; }); // should ok
|
||||
var evenSquaresInline = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(function (x) { return x % 2 === 0 ? x * x : null; })
|
||||
.filter(function (x) { return !!x; }); // tests truthiness, not non-nullishness
|
||||
var isTruthy = function (x) { return !!x; };
|
||||
var evenSquares = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(function (x) { return x % 2 === 0 ? x * x : null; })
|
||||
.filter(isTruthy);
|
||||
var evenSquaresNonNull = // should ok
|
||||
[1, 2, 3, 4]
|
||||
.map(function (x) { return x % 2 === 0 ? x * x : null; })
|
||||
.filter(function (x) { return x !== null; });
|
||||
function isNonNull(x) {
|
||||
return x !== null;
|
||||
}
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x) {
|
||||
var ok = x !== null;
|
||||
return ok;
|
||||
}
|
||||
function isNonNullGeneric(x) {
|
||||
return x !== null;
|
||||
}
|
||||
// Type guards can flow between functions
|
||||
var myGuard = function (o) { return !!o; };
|
||||
var mySecondGuard = function (o) { return myGuard(o); };
|
||||
var myArray = [];
|
||||
var result = myArray
|
||||
.map(function (arr) { return arr.list; })
|
||||
.filter(function (arr) { return arr && arr.length; })
|
||||
.map(function (arr) { return arr // should error
|
||||
.filter(function (obj) { return obj && obj.data; })
|
||||
.map(function (obj) { return JSON.parse(obj.data); }); } // should error
|
||||
);
|
||||
var result2 = myArray
|
||||
.map(function (arr) { return arr.list; })
|
||||
.filter(function (arr) { return !!arr; })
|
||||
.filter(function (arr) { return arr.length; })
|
||||
.map(function (arr) { return arr // should ok
|
||||
.filter(function (obj) { return obj; })
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(function (obj) { return !!obj.data; })
|
||||
.map(function (obj) { return JSON.parse(obj.data); }); });
|
||||
var list = [];
|
||||
var resultBars = list.filter(function (value) { return 'bar' in value; }); // should ok
|
||||
function isBarNonNull(x) {
|
||||
return ('bar' in x);
|
||||
}
|
||||
var fooOrBar = list[0];
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
var t = fooOrBar; // should ok
|
||||
}
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
var a = [1, "foo", 2, "bar"].filter(function (x) { return typeof x === "string"; });
|
||||
a.push(10);
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
if (isString(strOrNum)) {
|
||||
var t = strOrNum; // should ok
|
||||
}
|
||||
else {
|
||||
var t = strOrNum; // should ok
|
||||
}
|
||||
function flakyIsString(x) {
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
var t = strOrNum; // should error
|
||||
}
|
||||
else {
|
||||
var t = strOrNum; // should error
|
||||
}
|
||||
function isDate(x) {
|
||||
return x instanceof Date;
|
||||
}
|
||||
function flakyIsDate(x) {
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
}
|
||||
if (isDate(maybeDate)) {
|
||||
var t = maybeDate; // should ok
|
||||
}
|
||||
else {
|
||||
var t = maybeDate; // should ok
|
||||
}
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
var t = maybeDate; // should error
|
||||
}
|
||||
else {
|
||||
var t = maybeDate; // should ok
|
||||
}
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x) {
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
return typeof x === 'string';
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x) {
|
||||
x = [Math.random() < 0.5 ? "string" : 123][0];
|
||||
return typeof x === 'string';
|
||||
}
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x, y) {
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
}
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x) {
|
||||
if (typeof x === 'string') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function guardsOneButNotOthers(a, b, c) {
|
||||
return typeof b === 'string';
|
||||
}
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x) {
|
||||
return typeof __x === 'string';
|
||||
}
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
var booleanIdentity = function (x) { return x; };
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
var numOrBoolean = function (x) { return typeof x === 'number' || x; };
|
||||
var Inferrer = /** @class */ (function () {
|
||||
function Inferrer() {
|
||||
}
|
||||
Inferrer.prototype.isNumber = function (x) {
|
||||
return typeof x === 'number';
|
||||
};
|
||||
return Inferrer;
|
||||
}());
|
||||
var inf = new Inferrer();
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
var t = numOrStr; // should ok
|
||||
}
|
||||
else {
|
||||
var t = numOrStr; // should ok
|
||||
}
|
||||
// Type predicates are not inferred on "this"
|
||||
var C1 = /** @class */ (function () {
|
||||
function C1() {
|
||||
}
|
||||
C1.prototype.isC2 = function () {
|
||||
return this instanceof C2;
|
||||
};
|
||||
return C1;
|
||||
}());
|
||||
var C2 = /** @class */ (function (_super) {
|
||||
__extends(C2, _super);
|
||||
function C2() {
|
||||
var _this = _super !== null && _super.apply(this, arguments) || this;
|
||||
_this.z = 0;
|
||||
return _this;
|
||||
}
|
||||
return C2;
|
||||
}(C1));
|
||||
if (c.isC2()) {
|
||||
var c2 = c; // should error
|
||||
}
|
||||
function doNotRefineDestructuredParam(_a) {
|
||||
var x = _a.x, y = _a.y;
|
||||
return typeof x === 'number';
|
||||
}
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x) {
|
||||
return typeof x === "string" && x.length < 10;
|
||||
}
|
||||
if (isShortString(str)) {
|
||||
str.charAt(0); // should ok
|
||||
}
|
||||
else {
|
||||
str.charAt(0); // should ok
|
||||
}
|
||||
function isStringFromUnknown(x) {
|
||||
return typeof x === "string";
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
str.charAt(0); // should OK
|
||||
}
|
||||
else {
|
||||
var t = str; // should OK
|
||||
}
|
||||
// infer a union type
|
||||
function isNumOrStr(x) {
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
}
|
||||
if (isNumOrStr(unk)) {
|
||||
var t = unk; // should ok
|
||||
}
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x) {
|
||||
if (x instanceof Date) {
|
||||
throw new Error();
|
||||
}
|
||||
return typeof x === 'string';
|
||||
}
|
||||
if (assertAndPredicate(snd)) {
|
||||
var t = snd; // should error
|
||||
}
|
||||
function isNumberWithThis(x) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
function narrowFromAny(x) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
var noInferenceFromRest = function () {
|
||||
var f = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
f[_i] = arguments[_i];
|
||||
}
|
||||
return f[0] === "a";
|
||||
};
|
||||
var noInferenceFromImpossibleRest = function () {
|
||||
var f = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
f[_i] = arguments[_i];
|
||||
}
|
||||
return typeof f === "undefined";
|
||||
};
|
||||
function inferWithRest(x) {
|
||||
var f = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
f[_i - 1] = arguments[_i];
|
||||
}
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
|
||||
//// [inferTypePredicates.d.ts]
|
||||
declare const numsOrNull: (number | null)[];
|
||||
declare const filteredNumsTruthy: number[];
|
||||
declare const filteredNumsNonNullish: number[];
|
||||
declare const evenSquaresInline: number[];
|
||||
declare const isTruthy: (x: number | null) => boolean;
|
||||
declare const evenSquares: number[];
|
||||
declare const evenSquaresNonNull: number[];
|
||||
declare function isNonNull(x: number | null): x is number;
|
||||
declare function isNonNullVar(x: number | null): x is number;
|
||||
declare function isNonNullGeneric<T>(x: T): x is T & ({} | undefined);
|
||||
declare const myGuard: (o: string | undefined) => o is string;
|
||||
declare const mySecondGuard: (o: string | undefined) => o is string;
|
||||
type MyObj = {
|
||||
data?: string;
|
||||
};
|
||||
type MyArray = {
|
||||
list?: MyObj[];
|
||||
}[];
|
||||
declare const myArray: MyArray;
|
||||
declare const result: any[][];
|
||||
declare const result2: any[][];
|
||||
type Foo = {
|
||||
foo: string;
|
||||
};
|
||||
type Bar = Foo & {
|
||||
bar: string;
|
||||
};
|
||||
declare const list: (Foo | Bar)[];
|
||||
declare const resultBars: Bar[];
|
||||
declare function isBarNonNull(x: Foo | Bar | null): x is Bar;
|
||||
declare const fooOrBar: Foo | Bar;
|
||||
declare const a: string[];
|
||||
declare function backwardsGuard(x: number | string): x is number;
|
||||
declare function isString(x: string | number): x is string;
|
||||
declare let strOrNum: string | number;
|
||||
declare function flakyIsString(x: string | number): boolean;
|
||||
declare function isDate(x: object): x is Date;
|
||||
declare function flakyIsDate(x: object): boolean;
|
||||
declare let maybeDate: object;
|
||||
declare function irrelevantIsNumber(x: string | number): boolean;
|
||||
declare function irrelevantIsNumberDestructuring(x: string | number): boolean;
|
||||
declare function areBothNums(x: string | number, y: string | number): boolean;
|
||||
declare function doubleReturn(x: string | number): boolean;
|
||||
declare function guardsOneButNotOthers(a: string | number, b: string | number, c: string | number): b is string;
|
||||
declare function dunderguard(__x: number | string): __x is string;
|
||||
declare const booleanIdentity: (x: boolean) => boolean;
|
||||
declare const numOrBoolean: (x: number | boolean) => x is number | true;
|
||||
interface NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
}
|
||||
declare class Inferrer implements NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
declare const inf: Inferrer;
|
||||
declare class C1 {
|
||||
isC2(): boolean;
|
||||
}
|
||||
declare class C2 extends C1 {
|
||||
z: number;
|
||||
}
|
||||
declare let c: C1;
|
||||
declare function doNotRefineDestructuredParam({ x, y }: {
|
||||
x: number | null;
|
||||
y: number;
|
||||
}): boolean;
|
||||
declare function isShortString(x: unknown): boolean;
|
||||
declare let str: string;
|
||||
declare function isStringFromUnknown(x: unknown): x is string;
|
||||
declare function isNumOrStr(x: unknown): x is string | number;
|
||||
declare let unk: unknown;
|
||||
declare function assertAndPredicate(x: string | number | Date): x is string;
|
||||
declare let snd: string | number | Date;
|
||||
declare function isNumberWithThis(this: Date, x: number | string): x is number;
|
||||
declare function narrowFromAny(x: any): x is number;
|
||||
declare const noInferenceFromRest: (f_0: "a" | "b") => boolean;
|
||||
declare const noInferenceFromImpossibleRest: () => boolean;
|
||||
declare function inferWithRest(x: string | null, ...f: ["a", "b"]): x is string;
|
||||
749
tests/baselines/reference/inferTypePredicates.symbols
Normal file
749
tests/baselines/reference/inferTypePredicates.symbols
Normal file
@ -0,0 +1,749 @@
|
||||
//// [tests/cases/compiler/inferTypePredicates.ts] ////
|
||||
|
||||
=== inferTypePredicates.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
|
||||
const numsOrNull = [1, 2, 3, 4, null];
|
||||
>numsOrNull : Symbol(numsOrNull, Decl(inferTypePredicates.ts, 2, 5))
|
||||
|
||||
const filteredNumsTruthy: number[] = numsOrNull.filter(x => !!x); // should error
|
||||
>filteredNumsTruthy : Symbol(filteredNumsTruthy, Decl(inferTypePredicates.ts, 3, 5))
|
||||
>numsOrNull.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>numsOrNull : Symbol(numsOrNull, Decl(inferTypePredicates.ts, 2, 5))
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 3, 55))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 3, 55))
|
||||
|
||||
const filteredNumsNonNullish: number[] = numsOrNull.filter(x => x !== null); // should ok
|
||||
>filteredNumsNonNullish : Symbol(filteredNumsNonNullish, Decl(inferTypePredicates.ts, 4, 5))
|
||||
>numsOrNull.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>numsOrNull : Symbol(numsOrNull, Decl(inferTypePredicates.ts, 2, 5))
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 4, 59))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 4, 59))
|
||||
|
||||
const evenSquaresInline: number[] = // should error
|
||||
>evenSquaresInline : Symbol(evenSquaresInline, Decl(inferTypePredicates.ts, 6, 5))
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>[1, 2, 3, 4] .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 8, 13))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 8, 13))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 8, 13))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 8, 13))
|
||||
|
||||
.filter(x => !!x); // tests truthiness, not non-nullishness
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 9, 16))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 9, 16))
|
||||
|
||||
const isTruthy = (x: number | null) => !!x;
|
||||
>isTruthy : Symbol(isTruthy, Decl(inferTypePredicates.ts, 11, 5))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 11, 18))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 11, 18))
|
||||
|
||||
const evenSquares: number[] = // should error
|
||||
>evenSquares : Symbol(evenSquares, Decl(inferTypePredicates.ts, 13, 5))
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>[1, 2, 3, 4] .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 15, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 15, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 15, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 15, 9))
|
||||
|
||||
.filter(isTruthy);
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>isTruthy : Symbol(isTruthy, Decl(inferTypePredicates.ts, 11, 5))
|
||||
|
||||
const evenSquaresNonNull: number[] = // should ok
|
||||
>evenSquaresNonNull : Symbol(evenSquaresNonNull, Decl(inferTypePredicates.ts, 18, 5))
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>[1, 2, 3, 4] .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 20, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 20, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 20, 9))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 20, 9))
|
||||
|
||||
.filter(x => x !== null);
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 21, 12))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 21, 12))
|
||||
|
||||
function isNonNull(x: number | null) {
|
||||
>isNonNull : Symbol(isNonNull, Decl(inferTypePredicates.ts, 21, 29))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 23, 19))
|
||||
|
||||
return x !== null;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 23, 19))
|
||||
}
|
||||
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x: number | null) {
|
||||
>isNonNullVar : Symbol(isNonNullVar, Decl(inferTypePredicates.ts, 25, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 28, 22))
|
||||
|
||||
const ok = x !== null;
|
||||
>ok : Symbol(ok, Decl(inferTypePredicates.ts, 29, 7))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 28, 22))
|
||||
|
||||
return ok;
|
||||
>ok : Symbol(ok, Decl(inferTypePredicates.ts, 29, 7))
|
||||
}
|
||||
|
||||
function isNonNullGeneric<T>(x: T) {
|
||||
>isNonNullGeneric : Symbol(isNonNullGeneric, Decl(inferTypePredicates.ts, 31, 1))
|
||||
>T : Symbol(T, Decl(inferTypePredicates.ts, 33, 26))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 33, 29))
|
||||
>T : Symbol(T, Decl(inferTypePredicates.ts, 33, 26))
|
||||
|
||||
return x !== null;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 33, 29))
|
||||
}
|
||||
|
||||
// Type guards can flow between functions
|
||||
const myGuard = (o: string | undefined): o is string => !!o;
|
||||
>myGuard : Symbol(myGuard, Decl(inferTypePredicates.ts, 38, 5))
|
||||
>o : Symbol(o, Decl(inferTypePredicates.ts, 38, 17))
|
||||
>o : Symbol(o, Decl(inferTypePredicates.ts, 38, 17))
|
||||
>o : Symbol(o, Decl(inferTypePredicates.ts, 38, 17))
|
||||
|
||||
const mySecondGuard = (o: string | undefined) => myGuard(o);
|
||||
>mySecondGuard : Symbol(mySecondGuard, Decl(inferTypePredicates.ts, 39, 5))
|
||||
>o : Symbol(o, Decl(inferTypePredicates.ts, 39, 23))
|
||||
>myGuard : Symbol(myGuard, Decl(inferTypePredicates.ts, 38, 5))
|
||||
>o : Symbol(o, Decl(inferTypePredicates.ts, 39, 23))
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1327449914
|
||||
// This doesn't work because the false condition prevents type guard inference.
|
||||
// Breaking up the filters does work.
|
||||
type MyObj = { data?: string };
|
||||
>MyObj : Symbol(MyObj, Decl(inferTypePredicates.ts, 39, 60))
|
||||
>data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
|
||||
type MyArray = { list?: MyObj[] }[];
|
||||
>MyArray : Symbol(MyArray, Decl(inferTypePredicates.ts, 44, 31))
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 45, 16))
|
||||
>MyObj : Symbol(MyObj, Decl(inferTypePredicates.ts, 39, 60))
|
||||
|
||||
const myArray: MyArray = [];
|
||||
>myArray : Symbol(myArray, Decl(inferTypePredicates.ts, 46, 5))
|
||||
>MyArray : Symbol(MyArray, Decl(inferTypePredicates.ts, 44, 31))
|
||||
|
||||
const result = myArray
|
||||
>result : Symbol(result, Decl(inferTypePredicates.ts, 48, 5))
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => arr && arr.length) .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>myArray .map((arr) => arr.list) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>myArray .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>myArray : Symbol(myArray, Decl(inferTypePredicates.ts, 46, 5))
|
||||
|
||||
.map((arr) => arr.list)
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 49, 8))
|
||||
>arr.list : Symbol(list, Decl(inferTypePredicates.ts, 45, 16))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 49, 8))
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 45, 16))
|
||||
|
||||
.filter((arr) => arr && arr.length)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 50, 11))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 50, 11))
|
||||
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 50, 11))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
.map((arr) => arr // should error
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 51, 8))
|
||||
>arr // should error .filter((obj) => obj && obj.data) .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr // should error .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 51, 8))
|
||||
|
||||
.filter((obj) => obj && obj.data)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 52, 13))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 52, 13))
|
||||
>obj.data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 52, 13))
|
||||
>data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
|
||||
.map(obj => JSON.parse(obj.data)) // should error
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 53, 9))
|
||||
>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --))
|
||||
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --))
|
||||
>obj.data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 53, 9))
|
||||
>data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
|
||||
);
|
||||
|
||||
const result2 = myArray
|
||||
>result2 : Symbol(result2, Decl(inferTypePredicates.ts, 56, 5))
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter(arr => arr.length) .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>myArray .map((arr) => arr.list) .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>myArray .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>myArray : Symbol(myArray, Decl(inferTypePredicates.ts, 46, 5))
|
||||
|
||||
.map((arr) => arr.list)
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 57, 8))
|
||||
>arr.list : Symbol(list, Decl(inferTypePredicates.ts, 45, 16))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 57, 8))
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 45, 16))
|
||||
|
||||
.filter((arr) => !!arr)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 58, 11))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 58, 11))
|
||||
|
||||
.filter(arr => arr.length)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 59, 10))
|
||||
>arr.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 59, 10))
|
||||
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
.map((arr) => arr // should ok
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 60, 8))
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) .map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr // should ok .filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>arr : Symbol(arr, Decl(inferTypePredicates.ts, 60, 8))
|
||||
|
||||
.filter((obj) => obj)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 61, 13))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 61, 13))
|
||||
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(obj => !!obj.data)
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 63, 12))
|
||||
>obj.data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 63, 12))
|
||||
>data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
|
||||
.map(obj => JSON.parse(obj.data))
|
||||
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 64, 9))
|
||||
>JSON.parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --))
|
||||
>JSON : Symbol(JSON, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>parse : Symbol(JSON.parse, Decl(lib.es5.d.ts, --, --))
|
||||
>obj.data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
>obj : Symbol(obj, Decl(inferTypePredicates.ts, 64, 9))
|
||||
>data : Symbol(data, Decl(inferTypePredicates.ts, 44, 14))
|
||||
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1183547889
|
||||
type Foo = {
|
||||
>Foo : Symbol(Foo, Decl(inferTypePredicates.ts, 65, 4))
|
||||
|
||||
foo: string;
|
||||
>foo : Symbol(foo, Decl(inferTypePredicates.ts, 68, 12))
|
||||
}
|
||||
type Bar = Foo & {
|
||||
>Bar : Symbol(Bar, Decl(inferTypePredicates.ts, 70, 1))
|
||||
>Foo : Symbol(Foo, Decl(inferTypePredicates.ts, 65, 4))
|
||||
|
||||
bar: string;
|
||||
>bar : Symbol(bar, Decl(inferTypePredicates.ts, 71, 18))
|
||||
}
|
||||
|
||||
const list: (Foo | Bar)[] = [];
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 75, 5))
|
||||
>Foo : Symbol(Foo, Decl(inferTypePredicates.ts, 65, 4))
|
||||
>Bar : Symbol(Bar, Decl(inferTypePredicates.ts, 70, 1))
|
||||
|
||||
const resultBars: Bar[] = list.filter((value) => 'bar' in value); // should ok
|
||||
>resultBars : Symbol(resultBars, Decl(inferTypePredicates.ts, 76, 5))
|
||||
>Bar : Symbol(Bar, Decl(inferTypePredicates.ts, 70, 1))
|
||||
>list.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 75, 5))
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>value : Symbol(value, Decl(inferTypePredicates.ts, 76, 39))
|
||||
>value : Symbol(value, Decl(inferTypePredicates.ts, 76, 39))
|
||||
|
||||
function isBarNonNull(x: Foo | Bar | null) {
|
||||
>isBarNonNull : Symbol(isBarNonNull, Decl(inferTypePredicates.ts, 76, 65))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 78, 22))
|
||||
>Foo : Symbol(Foo, Decl(inferTypePredicates.ts, 65, 4))
|
||||
>Bar : Symbol(Bar, Decl(inferTypePredicates.ts, 70, 1))
|
||||
|
||||
return ('bar' in x!);
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 78, 22))
|
||||
}
|
||||
const fooOrBar = list[0];
|
||||
>fooOrBar : Symbol(fooOrBar, Decl(inferTypePredicates.ts, 81, 5))
|
||||
>list : Symbol(list, Decl(inferTypePredicates.ts, 75, 5))
|
||||
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
>isBarNonNull : Symbol(isBarNonNull, Decl(inferTypePredicates.ts, 76, 65))
|
||||
>fooOrBar : Symbol(fooOrBar, Decl(inferTypePredicates.ts, 81, 5))
|
||||
|
||||
const t: Bar = fooOrBar; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 83, 7))
|
||||
>Bar : Symbol(Bar, Decl(inferTypePredicates.ts, 70, 1))
|
||||
>fooOrBar : Symbol(fooOrBar, Decl(inferTypePredicates.ts, 81, 5))
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
const a = [1, "foo", 2, "bar"].filter(x => typeof x === "string");
|
||||
>a : Symbol(a, Decl(inferTypePredicates.ts, 88, 5))
|
||||
>[1, "foo", 2, "bar"].filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 88, 38))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 88, 38))
|
||||
|
||||
a.push(10);
|
||||
>a.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(inferTypePredicates.ts, 88, 5))
|
||||
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x: number|string): x is number {
|
||||
>backwardsGuard : Symbol(backwardsGuard, Decl(inferTypePredicates.ts, 89, 11))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 92, 24))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 92, 24))
|
||||
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 92, 24))
|
||||
}
|
||||
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x: string | number) {
|
||||
>isString : Symbol(isString, Decl(inferTypePredicates.ts, 94, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 97, 18))
|
||||
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 97, 18))
|
||||
}
|
||||
|
||||
declare let strOrNum: string | number;
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
|
||||
if (isString(strOrNum)) {
|
||||
>isString : Symbol(isString, Decl(inferTypePredicates.ts, 94, 1))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
|
||||
let t: string = strOrNum; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 103, 5))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
|
||||
} else {
|
||||
let t: number = strOrNum; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 105, 5))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
}
|
||||
|
||||
function flakyIsString(x: string | number) {
|
||||
>flakyIsString : Symbol(flakyIsString, Decl(inferTypePredicates.ts, 106, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 108, 23))
|
||||
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 108, 23))
|
||||
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
>flakyIsString : Symbol(flakyIsString, Decl(inferTypePredicates.ts, 106, 1))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
|
||||
let t: string = strOrNum; // should error
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 112, 5))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
|
||||
} else {
|
||||
let t: number = strOrNum; // should error
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 114, 5))
|
||||
>strOrNum : Symbol(strOrNum, Decl(inferTypePredicates.ts, 101, 11))
|
||||
}
|
||||
|
||||
function isDate(x: object) {
|
||||
>isDate : Symbol(isDate, Decl(inferTypePredicates.ts, 115, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 117, 16))
|
||||
|
||||
return x instanceof Date;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 117, 16))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
}
|
||||
function flakyIsDate(x: object) {
|
||||
>flakyIsDate : Symbol(flakyIsDate, Decl(inferTypePredicates.ts, 119, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 120, 21))
|
||||
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 120, 21))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
declare let maybeDate: object;
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
|
||||
if (isDate(maybeDate)) {
|
||||
>isDate : Symbol(isDate, Decl(inferTypePredicates.ts, 115, 1))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
|
||||
let t: Date = maybeDate; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 126, 5))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 128, 5))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
}
|
||||
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
>flakyIsDate : Symbol(flakyIsDate, Decl(inferTypePredicates.ts, 119, 1))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
|
||||
let t: Date = maybeDate; // should error
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 132, 5))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 134, 5))
|
||||
>maybeDate : Symbol(maybeDate, Decl(inferTypePredicates.ts, 124, 11))
|
||||
}
|
||||
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x: string | number) {
|
||||
>irrelevantIsNumber : Symbol(irrelevantIsNumber, Decl(inferTypePredicates.ts, 135, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 139, 28))
|
||||
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 139, 28))
|
||||
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 139, 28))
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x: string | number) {
|
||||
>irrelevantIsNumberDestructuring : Symbol(irrelevantIsNumberDestructuring, Decl(inferTypePredicates.ts, 142, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 143, 41))
|
||||
|
||||
[x] = [Math.random() < 0.5 ? "string" : 123];
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 143, 41))
|
||||
>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 143, 41))
|
||||
}
|
||||
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x: string|number, y: string|number) {
|
||||
>areBothNums : Symbol(areBothNums, Decl(inferTypePredicates.ts, 146, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 149, 21))
|
||||
>y : Symbol(y, Decl(inferTypePredicates.ts, 149, 38))
|
||||
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 149, 21))
|
||||
>y : Symbol(y, Decl(inferTypePredicates.ts, 149, 38))
|
||||
}
|
||||
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x: string|number) {
|
||||
>doubleReturn : Symbol(doubleReturn, Decl(inferTypePredicates.ts, 151, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 154, 22))
|
||||
|
||||
if (typeof x === 'string') {
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 154, 22))
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function guardsOneButNotOthers(a: string|number, b: string|number, c: string|number) {
|
||||
>guardsOneButNotOthers : Symbol(guardsOneButNotOthers, Decl(inferTypePredicates.ts, 159, 1))
|
||||
>a : Symbol(a, Decl(inferTypePredicates.ts, 161, 31))
|
||||
>b : Symbol(b, Decl(inferTypePredicates.ts, 161, 48))
|
||||
>c : Symbol(c, Decl(inferTypePredicates.ts, 161, 66))
|
||||
|
||||
return typeof b === 'string';
|
||||
>b : Symbol(b, Decl(inferTypePredicates.ts, 161, 48))
|
||||
}
|
||||
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x: number | string) {
|
||||
>dunderguard : Symbol(dunderguard, Decl(inferTypePredicates.ts, 163, 1))
|
||||
>__x : Symbol(__x, Decl(inferTypePredicates.ts, 166, 21))
|
||||
|
||||
return typeof __x === 'string';
|
||||
>__x : Symbol(__x, Decl(inferTypePredicates.ts, 166, 21))
|
||||
}
|
||||
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
const booleanIdentity = (x: boolean) => x;
|
||||
>booleanIdentity : Symbol(booleanIdentity, Decl(inferTypePredicates.ts, 171, 5))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 171, 25))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 171, 25))
|
||||
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
const numOrBoolean = (x: number | boolean) => typeof x === 'number' || x;
|
||||
>numOrBoolean : Symbol(numOrBoolean, Decl(inferTypePredicates.ts, 174, 5))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 174, 22))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 174, 22))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 174, 22))
|
||||
|
||||
// inferred guards in methods
|
||||
interface NumberInferrer {
|
||||
>NumberInferrer : Symbol(NumberInferrer, Decl(inferTypePredicates.ts, 174, 73))
|
||||
|
||||
isNumber(x: number | string): x is number;
|
||||
>isNumber : Symbol(NumberInferrer.isNumber, Decl(inferTypePredicates.ts, 177, 26))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 178, 11))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 178, 11))
|
||||
}
|
||||
class Inferrer implements NumberInferrer {
|
||||
>Inferrer : Symbol(Inferrer, Decl(inferTypePredicates.ts, 179, 1))
|
||||
>NumberInferrer : Symbol(NumberInferrer, Decl(inferTypePredicates.ts, 174, 73))
|
||||
|
||||
isNumber(x: number | string) { // should ok
|
||||
>isNumber : Symbol(Inferrer.isNumber, Decl(inferTypePredicates.ts, 180, 42))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 181, 11))
|
||||
|
||||
return typeof x === 'number';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 181, 11))
|
||||
}
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
>numOrStr : Symbol(numOrStr, Decl(inferTypePredicates.ts, 185, 11))
|
||||
|
||||
const inf = new Inferrer();
|
||||
>inf : Symbol(inf, Decl(inferTypePredicates.ts, 186, 5))
|
||||
>Inferrer : Symbol(Inferrer, Decl(inferTypePredicates.ts, 179, 1))
|
||||
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
>inf.isNumber : Symbol(Inferrer.isNumber, Decl(inferTypePredicates.ts, 180, 42))
|
||||
>inf : Symbol(inf, Decl(inferTypePredicates.ts, 186, 5))
|
||||
>isNumber : Symbol(Inferrer.isNumber, Decl(inferTypePredicates.ts, 180, 42))
|
||||
>numOrStr : Symbol(numOrStr, Decl(inferTypePredicates.ts, 185, 11))
|
||||
|
||||
let t: number = numOrStr; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 188, 5))
|
||||
>numOrStr : Symbol(numOrStr, Decl(inferTypePredicates.ts, 185, 11))
|
||||
|
||||
} else {
|
||||
let t: string = numOrStr; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 190, 5))
|
||||
>numOrStr : Symbol(numOrStr, Decl(inferTypePredicates.ts, 185, 11))
|
||||
}
|
||||
|
||||
// Type predicates are not inferred on "this"
|
||||
class C1 {
|
||||
>C1 : Symbol(C1, Decl(inferTypePredicates.ts, 191, 1))
|
||||
|
||||
isC2() {
|
||||
>isC2 : Symbol(C1.isC2, Decl(inferTypePredicates.ts, 194, 10))
|
||||
|
||||
return this instanceof C2;
|
||||
>this : Symbol(C1, Decl(inferTypePredicates.ts, 191, 1))
|
||||
>C2 : Symbol(C2, Decl(inferTypePredicates.ts, 198, 1))
|
||||
}
|
||||
}
|
||||
class C2 extends C1 {
|
||||
>C2 : Symbol(C2, Decl(inferTypePredicates.ts, 198, 1))
|
||||
>C1 : Symbol(C1, Decl(inferTypePredicates.ts, 191, 1))
|
||||
|
||||
z = 0;
|
||||
>z : Symbol(C2.z, Decl(inferTypePredicates.ts, 199, 21))
|
||||
}
|
||||
declare let c: C1;
|
||||
>c : Symbol(c, Decl(inferTypePredicates.ts, 202, 11))
|
||||
>C1 : Symbol(C1, Decl(inferTypePredicates.ts, 191, 1))
|
||||
|
||||
if (c.isC2()) {
|
||||
>c.isC2 : Symbol(C1.isC2, Decl(inferTypePredicates.ts, 194, 10))
|
||||
>c : Symbol(c, Decl(inferTypePredicates.ts, 202, 11))
|
||||
>isC2 : Symbol(C1.isC2, Decl(inferTypePredicates.ts, 194, 10))
|
||||
|
||||
let c2: C2 = c; // should error
|
||||
>c2 : Symbol(c2, Decl(inferTypePredicates.ts, 204, 5))
|
||||
>C2 : Symbol(C2, Decl(inferTypePredicates.ts, 198, 1))
|
||||
>c : Symbol(c, Decl(inferTypePredicates.ts, 202, 11))
|
||||
}
|
||||
|
||||
function doNotRefineDestructuredParam({x, y}: {x: number | null, y: number}) {
|
||||
>doNotRefineDestructuredParam : Symbol(doNotRefineDestructuredParam, Decl(inferTypePredicates.ts, 205, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 207, 39))
|
||||
>y : Symbol(y, Decl(inferTypePredicates.ts, 207, 41))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 207, 47))
|
||||
>y : Symbol(y, Decl(inferTypePredicates.ts, 207, 64))
|
||||
|
||||
return typeof x === 'number';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 207, 39))
|
||||
}
|
||||
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x: unknown) {
|
||||
>isShortString : Symbol(isShortString, Decl(inferTypePredicates.ts, 209, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 212, 23))
|
||||
|
||||
return typeof x === "string" && x.length < 10;
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 212, 23))
|
||||
>x.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 212, 23))
|
||||
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
declare let str: string;
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
|
||||
if (isShortString(str)) {
|
||||
>isShortString : Symbol(isShortString, Decl(inferTypePredicates.ts, 209, 1))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
|
||||
str.charAt(0); // should ok
|
||||
>str.charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
>charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
} else {
|
||||
str.charAt(0); // should ok
|
||||
>str.charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
>charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
|
||||
function isStringFromUnknown(x: unknown) {
|
||||
>isStringFromUnknown : Symbol(isStringFromUnknown, Decl(inferTypePredicates.ts, 221, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 223, 29))
|
||||
|
||||
return typeof x === "string";
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 223, 29))
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
>isStringFromUnknown : Symbol(isStringFromUnknown, Decl(inferTypePredicates.ts, 221, 1))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
|
||||
str.charAt(0); // should OK
|
||||
>str.charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
>charAt : Symbol(String.charAt, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
} else {
|
||||
let t: never = str; // should OK
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 229, 5))
|
||||
>str : Symbol(str, Decl(inferTypePredicates.ts, 216, 11))
|
||||
}
|
||||
|
||||
// infer a union type
|
||||
function isNumOrStr(x: unknown) {
|
||||
>isNumOrStr : Symbol(isNumOrStr, Decl(inferTypePredicates.ts, 230, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 233, 20))
|
||||
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 233, 20))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 233, 20))
|
||||
}
|
||||
declare let unk: unknown;
|
||||
>unk : Symbol(unk, Decl(inferTypePredicates.ts, 236, 11))
|
||||
|
||||
if (isNumOrStr(unk)) {
|
||||
>isNumOrStr : Symbol(isNumOrStr, Decl(inferTypePredicates.ts, 230, 1))
|
||||
>unk : Symbol(unk, Decl(inferTypePredicates.ts, 236, 11))
|
||||
|
||||
let t: number | string = unk; // should ok
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 238, 5))
|
||||
>unk : Symbol(unk, Decl(inferTypePredicates.ts, 236, 11))
|
||||
}
|
||||
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x: string | number | Date) {
|
||||
>assertAndPredicate : Symbol(assertAndPredicate, Decl(inferTypePredicates.ts, 239, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 242, 28))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
|
||||
if (x instanceof Date) {
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 242, 28))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
|
||||
throw new Error();
|
||||
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 242, 28))
|
||||
}
|
||||
|
||||
declare let snd: string | number | Date;
|
||||
>snd : Symbol(snd, Decl(inferTypePredicates.ts, 249, 11))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
|
||||
if (assertAndPredicate(snd)) {
|
||||
>assertAndPredicate : Symbol(assertAndPredicate, Decl(inferTypePredicates.ts, 239, 1))
|
||||
>snd : Symbol(snd, Decl(inferTypePredicates.ts, 249, 11))
|
||||
|
||||
let t: string = snd; // should error
|
||||
>t : Symbol(t, Decl(inferTypePredicates.ts, 251, 5))
|
||||
>snd : Symbol(snd, Decl(inferTypePredicates.ts, 249, 11))
|
||||
}
|
||||
|
||||
function isNumberWithThis(this: Date, x: number | string) {
|
||||
>isNumberWithThis : Symbol(isNumberWithThis, Decl(inferTypePredicates.ts, 252, 1))
|
||||
>this : Symbol(this, Decl(inferTypePredicates.ts, 254, 26))
|
||||
>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 254, 37))
|
||||
|
||||
return typeof x === 'number';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 254, 37))
|
||||
}
|
||||
|
||||
function narrowFromAny(x: any) {
|
||||
>narrowFromAny : Symbol(narrowFromAny, Decl(inferTypePredicates.ts, 256, 1))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 258, 23))
|
||||
|
||||
return typeof x === 'number';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 258, 23))
|
||||
}
|
||||
|
||||
const noInferenceFromRest = (...f: ["a" | "b"]) => f[0] === "a";
|
||||
>noInferenceFromRest : Symbol(noInferenceFromRest, Decl(inferTypePredicates.ts, 262, 5))
|
||||
>f : Symbol(f, Decl(inferTypePredicates.ts, 262, 29))
|
||||
>f : Symbol(f, Decl(inferTypePredicates.ts, 262, 29))
|
||||
>0 : Symbol(0)
|
||||
|
||||
const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
|
||||
>noInferenceFromImpossibleRest : Symbol(noInferenceFromImpossibleRest, Decl(inferTypePredicates.ts, 263, 5))
|
||||
>f : Symbol(f, Decl(inferTypePredicates.ts, 263, 39))
|
||||
>f : Symbol(f, Decl(inferTypePredicates.ts, 263, 39))
|
||||
|
||||
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
|
||||
>inferWithRest : Symbol(inferWithRest, Decl(inferTypePredicates.ts, 263, 77))
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 265, 23))
|
||||
>f : Symbol(f, Decl(inferTypePredicates.ts, 265, 40))
|
||||
|
||||
return typeof x === 'string';
|
||||
>x : Symbol(x, Decl(inferTypePredicates.ts, 265, 23))
|
||||
}
|
||||
|
||||
981
tests/baselines/reference/inferTypePredicates.types
Normal file
981
tests/baselines/reference/inferTypePredicates.types
Normal file
@ -0,0 +1,981 @@
|
||||
//// [tests/cases/compiler/inferTypePredicates.ts] ////
|
||||
|
||||
=== inferTypePredicates.ts ===
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
|
||||
const numsOrNull = [1, 2, 3, 4, null];
|
||||
>numsOrNull : (number | null)[]
|
||||
>[1, 2, 3, 4, null] : (number | null)[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
>4 : 4
|
||||
|
||||
const filteredNumsTruthy: number[] = numsOrNull.filter(x => !!x); // should error
|
||||
>filteredNumsTruthy : number[]
|
||||
>numsOrNull.filter(x => !!x) : (number | null)[]
|
||||
>numsOrNull.filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>numsOrNull : (number | null)[]
|
||||
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>x => !!x : (x: number | null) => boolean
|
||||
>x : number | null
|
||||
>!!x : boolean
|
||||
>!x : boolean
|
||||
>x : number | null
|
||||
|
||||
const filteredNumsNonNullish: number[] = numsOrNull.filter(x => x !== null); // should ok
|
||||
>filteredNumsNonNullish : number[]
|
||||
>numsOrNull.filter(x => x !== null) : number[]
|
||||
>numsOrNull.filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>numsOrNull : (number | null)[]
|
||||
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>x => x !== null : (x: number | null) => x is number
|
||||
>x : number | null
|
||||
>x !== null : boolean
|
||||
>x : number | null
|
||||
|
||||
const evenSquaresInline: number[] = // should error
|
||||
>evenSquaresInline : number[]
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter(x => !!x) : (number | null)[]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) : (number | null)[]
|
||||
>[1, 2, 3, 4] .map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>[1, 2, 3, 4] : number[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
>4 : 4
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>x => x % 2 === 0 ? x * x : null : (x: number) => number | null
|
||||
>x : number
|
||||
>x % 2 === 0 ? x * x : null : number | null
|
||||
>x % 2 === 0 : boolean
|
||||
>x % 2 : number
|
||||
>x : number
|
||||
>2 : 2
|
||||
>0 : 0
|
||||
>x * x : number
|
||||
>x : number
|
||||
>x : number
|
||||
|
||||
.filter(x => !!x); // tests truthiness, not non-nullishness
|
||||
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>x => !!x : (x: number | null) => boolean
|
||||
>x : number | null
|
||||
>!!x : boolean
|
||||
>!x : boolean
|
||||
>x : number | null
|
||||
|
||||
const isTruthy = (x: number | null) => !!x;
|
||||
>isTruthy : (x: number | null) => boolean
|
||||
>(x: number | null) => !!x : (x: number | null) => boolean
|
||||
>x : number | null
|
||||
>!!x : boolean
|
||||
>!x : boolean
|
||||
>x : number | null
|
||||
|
||||
const evenSquares: number[] = // should error
|
||||
>evenSquares : number[]
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter(isTruthy) : (number | null)[]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) : (number | null)[]
|
||||
>[1, 2, 3, 4] .map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>[1, 2, 3, 4] : number[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
>4 : 4
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>x => x % 2 === 0 ? x * x : null : (x: number) => number | null
|
||||
>x : number
|
||||
>x % 2 === 0 ? x * x : null : number | null
|
||||
>x % 2 === 0 : boolean
|
||||
>x % 2 : number
|
||||
>x : number
|
||||
>2 : 2
|
||||
>0 : 0
|
||||
>x * x : number
|
||||
>x : number
|
||||
>x : number
|
||||
|
||||
.filter(isTruthy);
|
||||
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>isTruthy : (x: number | null) => boolean
|
||||
|
||||
const evenSquaresNonNull: number[] = // should ok
|
||||
>evenSquaresNonNull : number[]
|
||||
|
||||
[1, 2, 3, 4]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter(x => x !== null) : number[]
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) .filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>[1, 2, 3, 4] .map(x => x % 2 === 0 ? x * x : null) : (number | null)[]
|
||||
>[1, 2, 3, 4] .map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>[1, 2, 3, 4] : number[]
|
||||
>1 : 1
|
||||
>2 : 2
|
||||
>3 : 3
|
||||
>4 : 4
|
||||
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
>map : <U>(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any) => U[]
|
||||
>x => x % 2 === 0 ? x * x : null : (x: number) => number | null
|
||||
>x : number
|
||||
>x % 2 === 0 ? x * x : null : number | null
|
||||
>x % 2 === 0 : boolean
|
||||
>x % 2 : number
|
||||
>x : number
|
||||
>2 : 2
|
||||
>0 : 0
|
||||
>x * x : number
|
||||
>x : number
|
||||
>x : number
|
||||
|
||||
.filter(x => x !== null);
|
||||
>filter : { <S extends number | null>(predicate: (value: number | null, index: number, array: (number | null)[]) => value is S, thisArg?: any): S[]; (predicate: (value: number | null, index: number, array: (number | null)[]) => unknown, thisArg?: any): (number | null)[]; }
|
||||
>x => x !== null : (x: number | null) => x is number
|
||||
>x : number | null
|
||||
>x !== null : boolean
|
||||
>x : number | null
|
||||
|
||||
function isNonNull(x: number | null) {
|
||||
>isNonNull : (x: number | null) => x is number
|
||||
>x : number | null
|
||||
|
||||
return x !== null;
|
||||
>x !== null : boolean
|
||||
>x : number | null
|
||||
}
|
||||
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x: number | null) {
|
||||
>isNonNullVar : (x: number | null) => x is number
|
||||
>x : number | null
|
||||
|
||||
const ok = x !== null;
|
||||
>ok : boolean
|
||||
>x !== null : boolean
|
||||
>x : number | null
|
||||
|
||||
return ok;
|
||||
>ok : boolean
|
||||
}
|
||||
|
||||
function isNonNullGeneric<T>(x: T) {
|
||||
>isNonNullGeneric : <T>(x: T) => x is T & ({} | undefined)
|
||||
>x : T
|
||||
|
||||
return x !== null;
|
||||
>x !== null : boolean
|
||||
>x : T
|
||||
}
|
||||
|
||||
// Type guards can flow between functions
|
||||
const myGuard = (o: string | undefined): o is string => !!o;
|
||||
>myGuard : (o: string | undefined) => o is string
|
||||
>(o: string | undefined): o is string => !!o : (o: string | undefined) => o is string
|
||||
>o : string | undefined
|
||||
>!!o : boolean
|
||||
>!o : boolean
|
||||
>o : string | undefined
|
||||
|
||||
const mySecondGuard = (o: string | undefined) => myGuard(o);
|
||||
>mySecondGuard : (o: string | undefined) => o is string
|
||||
>(o: string | undefined) => myGuard(o) : (o: string | undefined) => o is string
|
||||
>o : string | undefined
|
||||
>myGuard(o) : boolean
|
||||
>myGuard : (o: string | undefined) => o is string
|
||||
>o : string | undefined
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1327449914
|
||||
// This doesn't work because the false condition prevents type guard inference.
|
||||
// Breaking up the filters does work.
|
||||
type MyObj = { data?: string };
|
||||
>MyObj : { data?: string | undefined; }
|
||||
>data : string | undefined
|
||||
|
||||
type MyArray = { list?: MyObj[] }[];
|
||||
>MyArray : { list?: MyObj[] | undefined; }[]
|
||||
>list : MyObj[] | undefined
|
||||
|
||||
const myArray: MyArray = [];
|
||||
>myArray : MyArray
|
||||
>[] : never[]
|
||||
|
||||
const result = myArray
|
||||
>result : any[][]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => arr && arr.length) .map((arr) => arr // should error .filter((obj) => obj && obj.data) .map(obj => JSON.parse(obj.data)) // should error ) : any[][]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => arr && arr.length) .map : <U>(callbackfn: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => U, thisArg?: any) => U[]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => arr && arr.length) : (MyObj[] | undefined)[]
|
||||
>myArray .map((arr) => arr.list) .filter : { <S extends MyObj[] | undefined>(predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => unknown, thisArg?: any): (MyObj[] | undefined)[]; }
|
||||
>myArray .map((arr) => arr.list) : (MyObj[] | undefined)[]
|
||||
>myArray .map : <U>(callbackfn: (value: { list?: MyObj[] | undefined; }, index: number, array: { list?: MyObj[] | undefined; }[]) => U, thisArg?: any) => U[]
|
||||
>myArray : MyArray
|
||||
|
||||
.map((arr) => arr.list)
|
||||
>map : <U>(callbackfn: (value: { list?: MyObj[] | undefined; }, index: number, array: { list?: MyObj[] | undefined; }[]) => U, thisArg?: any) => U[]
|
||||
>(arr) => arr.list : (arr: { list?: MyObj[] | undefined; }) => MyObj[] | undefined
|
||||
>arr : { list?: MyObj[] | undefined; }
|
||||
>arr.list : MyObj[] | undefined
|
||||
>arr : { list?: MyObj[] | undefined; }
|
||||
>list : MyObj[] | undefined
|
||||
|
||||
.filter((arr) => arr && arr.length)
|
||||
>filter : { <S extends MyObj[] | undefined>(predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => unknown, thisArg?: any): (MyObj[] | undefined)[]; }
|
||||
>(arr) => arr && arr.length : (arr: MyObj[] | undefined) => number | undefined
|
||||
>arr : MyObj[] | undefined
|
||||
>arr && arr.length : number | undefined
|
||||
>arr : MyObj[] | undefined
|
||||
>arr.length : number
|
||||
>arr : MyObj[]
|
||||
>length : number
|
||||
|
||||
.map((arr) => arr // should error
|
||||
>map : <U>(callbackfn: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => U, thisArg?: any) => U[]
|
||||
>(arr) => arr // should error .filter((obj) => obj && obj.data) .map(obj => JSON.parse(obj.data)) : (arr: MyObj[] | undefined) => any[]
|
||||
>arr : MyObj[] | undefined
|
||||
>arr // should error .filter((obj) => obj && obj.data) .map(obj => JSON.parse(obj.data)) : any[]
|
||||
>arr // should error .filter((obj) => obj && obj.data) .map : <U>(callbackfn: (value: MyObj, index: number, array: MyObj[]) => U, thisArg?: any) => U[]
|
||||
>arr // should error .filter((obj) => obj && obj.data) : MyObj[]
|
||||
>arr // should error .filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>arr : MyObj[] | undefined
|
||||
|
||||
.filter((obj) => obj && obj.data)
|
||||
>filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>(obj) => obj && obj.data : (obj: MyObj) => string | undefined
|
||||
>obj : MyObj
|
||||
>obj && obj.data : string | undefined
|
||||
>obj : MyObj
|
||||
>obj.data : string | undefined
|
||||
>obj : MyObj
|
||||
>data : string | undefined
|
||||
|
||||
.map(obj => JSON.parse(obj.data)) // should error
|
||||
>map : <U>(callbackfn: (value: MyObj, index: number, array: MyObj[]) => U, thisArg?: any) => U[]
|
||||
>obj => JSON.parse(obj.data) : (obj: MyObj) => any
|
||||
>obj : MyObj
|
||||
>JSON.parse(obj.data) : any
|
||||
>JSON.parse : (text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => any
|
||||
>JSON : JSON
|
||||
>parse : (text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => any
|
||||
>obj.data : string | undefined
|
||||
>obj : MyObj
|
||||
>data : string | undefined
|
||||
|
||||
);
|
||||
|
||||
const result2 = myArray
|
||||
>result2 : any[][]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter(arr => arr.length) .map((arr) => arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) .map(obj => JSON.parse(obj.data)) ) : any[][]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter(arr => arr.length) .map : <U>(callbackfn: (value: MyObj[], index: number, array: MyObj[][]) => U, thisArg?: any) => U[]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter(arr => arr.length) : MyObj[][]
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) .filter : { <S extends MyObj[]>(predicate: (value: MyObj[], index: number, array: MyObj[][]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[], index: number, array: MyObj[][]) => unknown, thisArg?: any): MyObj[][]; }
|
||||
>myArray .map((arr) => arr.list) .filter((arr) => !!arr) : MyObj[][]
|
||||
>myArray .map((arr) => arr.list) .filter : { <S extends MyObj[] | undefined>(predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => unknown, thisArg?: any): (MyObj[] | undefined)[]; }
|
||||
>myArray .map((arr) => arr.list) : (MyObj[] | undefined)[]
|
||||
>myArray .map : <U>(callbackfn: (value: { list?: MyObj[] | undefined; }, index: number, array: { list?: MyObj[] | undefined; }[]) => U, thisArg?: any) => U[]
|
||||
>myArray : MyArray
|
||||
|
||||
.map((arr) => arr.list)
|
||||
>map : <U>(callbackfn: (value: { list?: MyObj[] | undefined; }, index: number, array: { list?: MyObj[] | undefined; }[]) => U, thisArg?: any) => U[]
|
||||
>(arr) => arr.list : (arr: { list?: MyObj[] | undefined; }) => MyObj[] | undefined
|
||||
>arr : { list?: MyObj[] | undefined; }
|
||||
>arr.list : MyObj[] | undefined
|
||||
>arr : { list?: MyObj[] | undefined; }
|
||||
>list : MyObj[] | undefined
|
||||
|
||||
.filter((arr) => !!arr)
|
||||
>filter : { <S extends MyObj[] | undefined>(predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[] | undefined, index: number, array: (MyObj[] | undefined)[]) => unknown, thisArg?: any): (MyObj[] | undefined)[]; }
|
||||
>(arr) => !!arr : (arr: MyObj[] | undefined) => arr is MyObj[]
|
||||
>arr : MyObj[] | undefined
|
||||
>!!arr : boolean
|
||||
>!arr : boolean
|
||||
>arr : MyObj[] | undefined
|
||||
|
||||
.filter(arr => arr.length)
|
||||
>filter : { <S extends MyObj[]>(predicate: (value: MyObj[], index: number, array: MyObj[][]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj[], index: number, array: MyObj[][]) => unknown, thisArg?: any): MyObj[][]; }
|
||||
>arr => arr.length : (arr: MyObj[]) => number
|
||||
>arr : MyObj[]
|
||||
>arr.length : number
|
||||
>arr : MyObj[]
|
||||
>length : number
|
||||
|
||||
.map((arr) => arr // should ok
|
||||
>map : <U>(callbackfn: (value: MyObj[], index: number, array: MyObj[][]) => U, thisArg?: any) => U[]
|
||||
>(arr) => arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) .map(obj => JSON.parse(obj.data)) : (arr: MyObj[]) => any[]
|
||||
>arr : MyObj[]
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) .map(obj => JSON.parse(obj.data)) : any[]
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) .map : <U>(callbackfn: (value: MyObj, index: number, array: MyObj[]) => U, thisArg?: any) => U[]
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter(obj => !!obj.data) : MyObj[]
|
||||
>arr // should ok .filter((obj) => obj) // inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384 .filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>arr // should ok .filter((obj) => obj) : MyObj[]
|
||||
>arr // should ok .filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>arr : MyObj[]
|
||||
|
||||
.filter((obj) => obj)
|
||||
>filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>(obj) => obj : (obj: MyObj) => MyObj
|
||||
>obj : MyObj
|
||||
>obj : MyObj
|
||||
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(obj => !!obj.data)
|
||||
>filter : { <S extends MyObj>(predicate: (value: MyObj, index: number, array: MyObj[]) => value is S, thisArg?: any): S[]; (predicate: (value: MyObj, index: number, array: MyObj[]) => unknown, thisArg?: any): MyObj[]; }
|
||||
>obj => !!obj.data : (obj: MyObj) => boolean
|
||||
>obj : MyObj
|
||||
>!!obj.data : boolean
|
||||
>!obj.data : boolean
|
||||
>obj.data : string | undefined
|
||||
>obj : MyObj
|
||||
>data : string | undefined
|
||||
|
||||
.map(obj => JSON.parse(obj.data))
|
||||
>map : <U>(callbackfn: (value: MyObj, index: number, array: MyObj[]) => U, thisArg?: any) => U[]
|
||||
>obj => JSON.parse(obj.data) : (obj: MyObj) => any
|
||||
>obj : MyObj
|
||||
>JSON.parse(obj.data) : any
|
||||
>JSON.parse : (text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => any
|
||||
>JSON : JSON
|
||||
>parse : (text: string, reviver?: ((this: any, key: string, value: any) => any) | undefined) => any
|
||||
>obj.data : string | undefined
|
||||
>obj : MyObj
|
||||
>data : string | undefined
|
||||
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1183547889
|
||||
type Foo = {
|
||||
>Foo : { foo: string; }
|
||||
|
||||
foo: string;
|
||||
>foo : string
|
||||
}
|
||||
type Bar = Foo & {
|
||||
>Bar : Foo & { bar: string; }
|
||||
|
||||
bar: string;
|
||||
>bar : string
|
||||
}
|
||||
|
||||
const list: (Foo | Bar)[] = [];
|
||||
>list : (Foo | Bar)[]
|
||||
>[] : never[]
|
||||
|
||||
const resultBars: Bar[] = list.filter((value) => 'bar' in value); // should ok
|
||||
>resultBars : Bar[]
|
||||
>list.filter((value) => 'bar' in value) : Bar[]
|
||||
>list.filter : { <S extends Foo | Bar>(predicate: (value: Foo | Bar, index: number, array: (Foo | Bar)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Foo | Bar, index: number, array: (Foo | Bar)[]) => unknown, thisArg?: any): (Foo | Bar)[]; }
|
||||
>list : (Foo | Bar)[]
|
||||
>filter : { <S extends Foo | Bar>(predicate: (value: Foo | Bar, index: number, array: (Foo | Bar)[]) => value is S, thisArg?: any): S[]; (predicate: (value: Foo | Bar, index: number, array: (Foo | Bar)[]) => unknown, thisArg?: any): (Foo | Bar)[]; }
|
||||
>(value) => 'bar' in value : (value: Foo | Bar) => value is Bar
|
||||
>value : Foo | Bar
|
||||
>'bar' in value : boolean
|
||||
>'bar' : "bar"
|
||||
>value : Foo | Bar
|
||||
|
||||
function isBarNonNull(x: Foo | Bar | null) {
|
||||
>isBarNonNull : (x: Foo | Bar | null) => x is Bar
|
||||
>x : Foo | Bar | null
|
||||
|
||||
return ('bar' in x!);
|
||||
>('bar' in x!) : boolean
|
||||
>'bar' in x! : boolean
|
||||
>'bar' : "bar"
|
||||
>x! : Foo | Bar
|
||||
>x : Foo | Bar | null
|
||||
}
|
||||
const fooOrBar = list[0];
|
||||
>fooOrBar : Foo | Bar
|
||||
>list[0] : Foo | Bar
|
||||
>list : (Foo | Bar)[]
|
||||
>0 : 0
|
||||
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
>isBarNonNull(fooOrBar) : boolean
|
||||
>isBarNonNull : (x: Foo | Bar | null) => x is Bar
|
||||
>fooOrBar : Foo | Bar
|
||||
|
||||
const t: Bar = fooOrBar; // should ok
|
||||
>t : Bar
|
||||
>fooOrBar : Bar
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
const a = [1, "foo", 2, "bar"].filter(x => typeof x === "string");
|
||||
>a : string[]
|
||||
>[1, "foo", 2, "bar"].filter(x => typeof x === "string") : string[]
|
||||
>[1, "foo", 2, "bar"].filter : { <S extends string | number>(predicate: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): (string | number)[]; }
|
||||
>[1, "foo", 2, "bar"] : (string | number)[]
|
||||
>1 : 1
|
||||
>"foo" : "foo"
|
||||
>2 : 2
|
||||
>"bar" : "bar"
|
||||
>filter : { <S extends string | number>(predicate: (value: string | number, index: number, array: (string | number)[]) => value is S, thisArg?: any): S[]; (predicate: (value: string | number, index: number, array: (string | number)[]) => unknown, thisArg?: any): (string | number)[]; }
|
||||
>x => typeof x === "string" : (x: string | number) => x is string
|
||||
>x : string | number
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>"string" : "string"
|
||||
|
||||
a.push(10);
|
||||
>a.push(10) : number
|
||||
>a.push : (...items: string[]) => number
|
||||
>a : string[]
|
||||
>push : (...items: string[]) => number
|
||||
>10 : 10
|
||||
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x: number|string): x is number {
|
||||
>backwardsGuard : (x: number | string) => x is number
|
||||
>x : string | number
|
||||
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x: string | number) {
|
||||
>isString : (x: string | number) => x is string
|
||||
>x : string | number
|
||||
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
declare let strOrNum: string | number;
|
||||
>strOrNum : string | number
|
||||
|
||||
if (isString(strOrNum)) {
|
||||
>isString(strOrNum) : boolean
|
||||
>isString : (x: string | number) => x is string
|
||||
>strOrNum : string | number
|
||||
|
||||
let t: string = strOrNum; // should ok
|
||||
>t : string
|
||||
>strOrNum : string
|
||||
|
||||
} else {
|
||||
let t: number = strOrNum; // should ok
|
||||
>t : number
|
||||
>strOrNum : number
|
||||
}
|
||||
|
||||
function flakyIsString(x: string | number) {
|
||||
>flakyIsString : (x: string | number) => boolean
|
||||
>x : string | number
|
||||
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
>typeof x === 'string' && Math.random() > 0.5 : boolean
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
>Math.random() > 0.5 : boolean
|
||||
>Math.random() : number
|
||||
>Math.random : () => number
|
||||
>Math : Math
|
||||
>random : () => number
|
||||
>0.5 : 0.5
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
>flakyIsString(strOrNum) : boolean
|
||||
>flakyIsString : (x: string | number) => boolean
|
||||
>strOrNum : string | number
|
||||
|
||||
let t: string = strOrNum; // should error
|
||||
>t : string
|
||||
>strOrNum : string | number
|
||||
|
||||
} else {
|
||||
let t: number = strOrNum; // should error
|
||||
>t : number
|
||||
>strOrNum : string | number
|
||||
}
|
||||
|
||||
function isDate(x: object) {
|
||||
>isDate : (x: object) => x is Date
|
||||
>x : object
|
||||
|
||||
return x instanceof Date;
|
||||
>x instanceof Date : boolean
|
||||
>x : object
|
||||
>Date : DateConstructor
|
||||
}
|
||||
function flakyIsDate(x: object) {
|
||||
>flakyIsDate : (x: object) => boolean
|
||||
>x : object
|
||||
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
>x instanceof Date && Math.random() > 0.5 : boolean
|
||||
>x instanceof Date : boolean
|
||||
>x : object
|
||||
>Date : DateConstructor
|
||||
>Math.random() > 0.5 : boolean
|
||||
>Math.random() : number
|
||||
>Math.random : () => number
|
||||
>Math : Math
|
||||
>random : () => number
|
||||
>0.5 : 0.5
|
||||
}
|
||||
|
||||
declare let maybeDate: object;
|
||||
>maybeDate : object
|
||||
|
||||
if (isDate(maybeDate)) {
|
||||
>isDate(maybeDate) : boolean
|
||||
>isDate : (x: object) => x is Date
|
||||
>maybeDate : object
|
||||
|
||||
let t: Date = maybeDate; // should ok
|
||||
>t : Date
|
||||
>maybeDate : Date
|
||||
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
>t : object
|
||||
>maybeDate : object
|
||||
}
|
||||
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
>flakyIsDate(maybeDate) : boolean
|
||||
>flakyIsDate : (x: object) => boolean
|
||||
>maybeDate : object
|
||||
|
||||
let t: Date = maybeDate; // should error
|
||||
>t : Date
|
||||
>maybeDate : object
|
||||
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
>t : object
|
||||
>maybeDate : object
|
||||
}
|
||||
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x: string | number) {
|
||||
>irrelevantIsNumber : (x: string | number) => boolean
|
||||
>x : string | number
|
||||
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
>x = Math.random() < 0.5 ? "string" : 123 : "string" | 123
|
||||
>x : string | number
|
||||
>Math.random() < 0.5 ? "string" : 123 : "string" | 123
|
||||
>Math.random() < 0.5 : boolean
|
||||
>Math.random() : number
|
||||
>Math.random : () => number
|
||||
>Math : Math
|
||||
>random : () => number
|
||||
>0.5 : 0.5
|
||||
>"string" : "string"
|
||||
>123 : 123
|
||||
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x: string | number) {
|
||||
>irrelevantIsNumberDestructuring : (x: string | number) => boolean
|
||||
>x : string | number
|
||||
|
||||
[x] = [Math.random() < 0.5 ? "string" : 123];
|
||||
>[x] = [Math.random() < 0.5 ? "string" : 123] : [string | number]
|
||||
>[x] : [string | number]
|
||||
>x : string | number
|
||||
>[Math.random() < 0.5 ? "string" : 123] : [string | number]
|
||||
>Math.random() < 0.5 ? "string" : 123 : "string" | 123
|
||||
>Math.random() < 0.5 : boolean
|
||||
>Math.random() : number
|
||||
>Math.random : () => number
|
||||
>Math : Math
|
||||
>random : () => number
|
||||
>0.5 : 0.5
|
||||
>"string" : "string"
|
||||
>123 : 123
|
||||
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x: string|number, y: string|number) {
|
||||
>areBothNums : (x: string | number, y: string | number) => boolean
|
||||
>x : string | number
|
||||
>y : string | number
|
||||
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
>typeof x === 'number' && typeof y === 'number' : boolean
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'number' : "number"
|
||||
>typeof y === 'number' : boolean
|
||||
>typeof y : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>y : string | number
|
||||
>'number' : "number"
|
||||
}
|
||||
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x: string|number) {
|
||||
>doubleReturn : (x: string | number) => boolean
|
||||
>x : string | number
|
||||
|
||||
if (typeof x === 'string') {
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
|
||||
return true;
|
||||
>true : true
|
||||
}
|
||||
return false;
|
||||
>false : false
|
||||
}
|
||||
|
||||
function guardsOneButNotOthers(a: string|number, b: string|number, c: string|number) {
|
||||
>guardsOneButNotOthers : (a: string | number, b: string | number, c: string | number) => b is string
|
||||
>a : string | number
|
||||
>b : string | number
|
||||
>c : string | number
|
||||
|
||||
return typeof b === 'string';
|
||||
>typeof b === 'string' : boolean
|
||||
>typeof b : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>b : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x: number | string) {
|
||||
>dunderguard : (__x: number | string) => __x is string
|
||||
>__x : string | number
|
||||
|
||||
return typeof __x === 'string';
|
||||
>typeof __x === 'string' : boolean
|
||||
>typeof __x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>__x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
const booleanIdentity = (x: boolean) => x;
|
||||
>booleanIdentity : (x: boolean) => boolean
|
||||
>(x: boolean) => x : (x: boolean) => boolean
|
||||
>x : boolean
|
||||
>x : boolean
|
||||
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
const numOrBoolean = (x: number | boolean) => typeof x === 'number' || x;
|
||||
>numOrBoolean : (x: number | boolean) => x is number | true
|
||||
>(x: number | boolean) => typeof x === 'number' || x : (x: number | boolean) => x is number | true
|
||||
>x : number | boolean
|
||||
>typeof x === 'number' || x : boolean
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : number | boolean
|
||||
>'number' : "number"
|
||||
>x : boolean
|
||||
|
||||
// inferred guards in methods
|
||||
interface NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
>isNumber : (x: number | string) => x is number
|
||||
>x : string | number
|
||||
}
|
||||
class Inferrer implements NumberInferrer {
|
||||
>Inferrer : Inferrer
|
||||
|
||||
isNumber(x: number | string) { // should ok
|
||||
>isNumber : (x: number | string) => x is number
|
||||
>x : string | number
|
||||
|
||||
return typeof x === 'number';
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'number' : "number"
|
||||
}
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
>numOrStr : string | number
|
||||
|
||||
const inf = new Inferrer();
|
||||
>inf : Inferrer
|
||||
>new Inferrer() : Inferrer
|
||||
>Inferrer : typeof Inferrer
|
||||
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
>inf.isNumber(numOrStr) : boolean
|
||||
>inf.isNumber : (x: string | number) => x is number
|
||||
>inf : Inferrer
|
||||
>isNumber : (x: string | number) => x is number
|
||||
>numOrStr : string | number
|
||||
|
||||
let t: number = numOrStr; // should ok
|
||||
>t : number
|
||||
>numOrStr : number
|
||||
|
||||
} else {
|
||||
let t: string = numOrStr; // should ok
|
||||
>t : string
|
||||
>numOrStr : string
|
||||
}
|
||||
|
||||
// Type predicates are not inferred on "this"
|
||||
class C1 {
|
||||
>C1 : C1
|
||||
|
||||
isC2() {
|
||||
>isC2 : () => boolean
|
||||
|
||||
return this instanceof C2;
|
||||
>this instanceof C2 : boolean
|
||||
>this : this
|
||||
>C2 : typeof C2
|
||||
}
|
||||
}
|
||||
class C2 extends C1 {
|
||||
>C2 : C2
|
||||
>C1 : C1
|
||||
|
||||
z = 0;
|
||||
>z : number
|
||||
>0 : 0
|
||||
}
|
||||
declare let c: C1;
|
||||
>c : C1
|
||||
|
||||
if (c.isC2()) {
|
||||
>c.isC2() : boolean
|
||||
>c.isC2 : () => boolean
|
||||
>c : C1
|
||||
>isC2 : () => boolean
|
||||
|
||||
let c2: C2 = c; // should error
|
||||
>c2 : C2
|
||||
>c : C1
|
||||
}
|
||||
|
||||
function doNotRefineDestructuredParam({x, y}: {x: number | null, y: number}) {
|
||||
>doNotRefineDestructuredParam : ({ x, y }: { x: number | null; y: number;}) => boolean
|
||||
>x : number | null
|
||||
>y : number
|
||||
>x : number | null
|
||||
>y : number
|
||||
|
||||
return typeof x === 'number';
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : number | null
|
||||
>'number' : "number"
|
||||
}
|
||||
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x: unknown) {
|
||||
>isShortString : (x: unknown) => boolean
|
||||
>x : unknown
|
||||
|
||||
return typeof x === "string" && x.length < 10;
|
||||
>typeof x === "string" && x.length < 10 : boolean
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"string" : "string"
|
||||
>x.length < 10 : boolean
|
||||
>x.length : number
|
||||
>x : string
|
||||
>length : number
|
||||
>10 : 10
|
||||
}
|
||||
|
||||
declare let str: string;
|
||||
>str : string
|
||||
|
||||
if (isShortString(str)) {
|
||||
>isShortString(str) : boolean
|
||||
>isShortString : (x: unknown) => boolean
|
||||
>str : string
|
||||
|
||||
str.charAt(0); // should ok
|
||||
>str.charAt(0) : string
|
||||
>str.charAt : (pos: number) => string
|
||||
>str : string
|
||||
>charAt : (pos: number) => string
|
||||
>0 : 0
|
||||
|
||||
} else {
|
||||
str.charAt(0); // should ok
|
||||
>str.charAt(0) : string
|
||||
>str.charAt : (pos: number) => string
|
||||
>str : string
|
||||
>charAt : (pos: number) => string
|
||||
>0 : 0
|
||||
}
|
||||
|
||||
function isStringFromUnknown(x: unknown) {
|
||||
>isStringFromUnknown : (x: unknown) => x is string
|
||||
>x : unknown
|
||||
|
||||
return typeof x === "string";
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"string" : "string"
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
>isStringFromUnknown(str) : boolean
|
||||
>isStringFromUnknown : (x: unknown) => x is string
|
||||
>str : string
|
||||
|
||||
str.charAt(0); // should OK
|
||||
>str.charAt(0) : string
|
||||
>str.charAt : (pos: number) => string
|
||||
>str : string
|
||||
>charAt : (pos: number) => string
|
||||
>0 : 0
|
||||
|
||||
} else {
|
||||
let t: never = str; // should OK
|
||||
>t : never
|
||||
>str : never
|
||||
}
|
||||
|
||||
// infer a union type
|
||||
function isNumOrStr(x: unknown) {
|
||||
>isNumOrStr : (x: unknown) => x is string | number
|
||||
>x : unknown
|
||||
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
>(typeof x === "number" || typeof x === "string") : boolean
|
||||
>typeof x === "number" || typeof x === "string" : boolean
|
||||
>typeof x === "number" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"number" : "number"
|
||||
>typeof x === "string" : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : unknown
|
||||
>"string" : "string"
|
||||
}
|
||||
declare let unk: unknown;
|
||||
>unk : unknown
|
||||
|
||||
if (isNumOrStr(unk)) {
|
||||
>isNumOrStr(unk) : boolean
|
||||
>isNumOrStr : (x: unknown) => x is string | number
|
||||
>unk : unknown
|
||||
|
||||
let t: number | string = unk; // should ok
|
||||
>t : string | number
|
||||
>unk : string | number
|
||||
}
|
||||
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x: string | number | Date) {
|
||||
>assertAndPredicate : (x: string | number | Date) => x is string
|
||||
>x : string | number | Date
|
||||
|
||||
if (x instanceof Date) {
|
||||
>x instanceof Date : boolean
|
||||
>x : string | number | Date
|
||||
>Date : DateConstructor
|
||||
|
||||
throw new Error();
|
||||
>new Error() : Error
|
||||
>Error : ErrorConstructor
|
||||
}
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
declare let snd: string | number | Date;
|
||||
>snd : string | number | Date
|
||||
|
||||
if (assertAndPredicate(snd)) {
|
||||
>assertAndPredicate(snd) : boolean
|
||||
>assertAndPredicate : (x: string | number | Date) => x is string
|
||||
>snd : string | number | Date
|
||||
|
||||
let t: string = snd; // should error
|
||||
>t : string
|
||||
>snd : string
|
||||
}
|
||||
|
||||
function isNumberWithThis(this: Date, x: number | string) {
|
||||
>isNumberWithThis : (this: Date, x: number | string) => x is number
|
||||
>this : Date
|
||||
>x : string | number
|
||||
|
||||
return typeof x === 'number';
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | number
|
||||
>'number' : "number"
|
||||
}
|
||||
|
||||
function narrowFromAny(x: any) {
|
||||
>narrowFromAny : (x: any) => x is number
|
||||
>x : any
|
||||
|
||||
return typeof x === 'number';
|
||||
>typeof x === 'number' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : any
|
||||
>'number' : "number"
|
||||
}
|
||||
|
||||
const noInferenceFromRest = (...f: ["a" | "b"]) => f[0] === "a";
|
||||
>noInferenceFromRest : (f_0: "a" | "b") => boolean
|
||||
>(...f: ["a" | "b"]) => f[0] === "a" : (f_0: "a" | "b") => boolean
|
||||
>f : ["a" | "b"]
|
||||
>f[0] === "a" : boolean
|
||||
>f[0] : "a" | "b"
|
||||
>f : ["a" | "b"]
|
||||
>0 : 0
|
||||
>"a" : "a"
|
||||
|
||||
const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
|
||||
>noInferenceFromImpossibleRest : () => boolean
|
||||
>(...f: []) => typeof f === "undefined" : () => boolean
|
||||
>f : []
|
||||
>typeof f === "undefined" : boolean
|
||||
>typeof f : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>f : []
|
||||
>"undefined" : "undefined"
|
||||
|
||||
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
|
||||
>inferWithRest : (x: string | null, f_0: "a", f_1: "b") => x is string
|
||||
>x : string | null
|
||||
>f : ["a", "b"]
|
||||
|
||||
return typeof x === 'string';
|
||||
>typeof x === 'string' : boolean
|
||||
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
|
||||
>x : string | null
|
||||
>'string' : "string"
|
||||
}
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
/src/a.js(10,7): error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray(arg: any): arg is any[]; readonly prototype: any[]; }'.
|
||||
Types of property 'isArray' are incompatible.
|
||||
Type '(arg: any) => boolean' is not assignable to type '(arg: any) => arg is any[]'.
|
||||
Signature '(arg: any): boolean' must be a type predicate.
|
||||
|
||||
|
||||
==== /src/a.js (1 errors) ====
|
||||
class Thing {
|
||||
static {
|
||||
this.doSomething = () => {};
|
||||
}
|
||||
}
|
||||
|
||||
Thing.doSomething();
|
||||
|
||||
// GH#46468
|
||||
class ElementsArray extends Array {
|
||||
~~~~~~~~~~~~~
|
||||
!!! error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray(arg: any): arg is any[]; readonly prototype: any[]; }'.
|
||||
!!! error TS2417: Types of property 'isArray' are incompatible.
|
||||
!!! error TS2417: Type '(arg: any) => boolean' is not assignable to type '(arg: any) => arg is any[]'.
|
||||
!!! error TS2417: Signature '(arg: any): boolean' must be a type predicate.
|
||||
static {
|
||||
const superisArray = super.isArray;
|
||||
const customIsArray = (arg)=> superisArray(arg);
|
||||
this.isArray = customIsArray;
|
||||
}
|
||||
}
|
||||
|
||||
ElementsArray.isArray(new ElementsArray());
|
||||
@ -33,27 +33,27 @@ class ElementsArray extends Array {
|
||||
>isArray : (arg: any) => arg is any[]
|
||||
|
||||
const customIsArray = (arg)=> superisArray(arg);
|
||||
>customIsArray : (arg: any) => boolean
|
||||
>(arg)=> superisArray(arg) : (arg: any) => boolean
|
||||
>customIsArray : (arg: any) => arg is any[]
|
||||
>(arg)=> superisArray(arg) : (arg: any) => arg is any[]
|
||||
>arg : any
|
||||
>superisArray(arg) : boolean
|
||||
>superisArray : (arg: any) => arg is any[]
|
||||
>arg : any
|
||||
|
||||
this.isArray = customIsArray;
|
||||
>this.isArray = customIsArray : (arg: any) => boolean
|
||||
>this.isArray : (arg: any) => boolean
|
||||
>this.isArray = customIsArray : (arg: any) => arg is any[]
|
||||
>this.isArray : (arg: any) => arg is any[]
|
||||
>this : typeof ElementsArray
|
||||
>isArray : (arg: any) => boolean
|
||||
>customIsArray : (arg: any) => boolean
|
||||
>isArray : (arg: any) => arg is any[]
|
||||
>customIsArray : (arg: any) => arg is any[]
|
||||
}
|
||||
}
|
||||
|
||||
ElementsArray.isArray(new ElementsArray());
|
||||
>ElementsArray.isArray(new ElementsArray()) : boolean
|
||||
>ElementsArray.isArray : (arg: any) => boolean
|
||||
>ElementsArray.isArray : (arg: any) => arg is any[]
|
||||
>ElementsArray : typeof ElementsArray
|
||||
>isArray : (arg: any) => boolean
|
||||
>isArray : (arg: any) => arg is any[]
|
||||
>new ElementsArray() : ElementsArray
|
||||
>ElementsArray : typeof ElementsArray
|
||||
|
||||
|
||||
@ -94,7 +94,7 @@ class PersonMixin extends Function {
|
||||
>Function : Function
|
||||
|
||||
public check(o: any) {
|
||||
>check : (o: any) => boolean
|
||||
>check : (o: any) => o is Person
|
||||
>o : any
|
||||
|
||||
return typeof o === "object" && o !== null && o instanceof Person;
|
||||
|
||||
19
tests/cases/compiler/circularConstructorWithReturn.ts
Normal file
19
tests/cases/compiler/circularConstructorWithReturn.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// @strictNullChecks: true
|
||||
// This should not be a circularity error. See
|
||||
// https://github.com/microsoft/TypeScript/pull/57465#issuecomment-1960271216
|
||||
export type Client = ReturnType<typeof getPrismaClient> extends new () => infer T ? T : never
|
||||
|
||||
export function getPrismaClient(options?: any) {
|
||||
class PrismaClient {
|
||||
self: Client;
|
||||
constructor(options?: any) {
|
||||
return (this.self = applyModelsAndClientExtensions(this));
|
||||
}
|
||||
}
|
||||
|
||||
return PrismaClient
|
||||
}
|
||||
|
||||
export function applyModelsAndClientExtensions(client: Client) {
|
||||
return client;
|
||||
}
|
||||
271
tests/cases/compiler/inferTypePredicates.ts
Normal file
271
tests/cases/compiler/inferTypePredicates.ts
Normal file
@ -0,0 +1,271 @@
|
||||
// @strictNullChecks: true
|
||||
// @declaration: true
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069
|
||||
|
||||
const numsOrNull = [1, 2, 3, 4, null];
|
||||
const filteredNumsTruthy: number[] = numsOrNull.filter(x => !!x); // should error
|
||||
const filteredNumsNonNullish: number[] = numsOrNull.filter(x => x !== null); // should ok
|
||||
|
||||
const evenSquaresInline: number[] = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => !!x); // tests truthiness, not non-nullishness
|
||||
|
||||
const isTruthy = (x: number | null) => !!x;
|
||||
|
||||
const evenSquares: number[] = // should error
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(isTruthy);
|
||||
|
||||
const evenSquaresNonNull: number[] = // should ok
|
||||
[1, 2, 3, 4]
|
||||
.map(x => x % 2 === 0 ? x * x : null)
|
||||
.filter(x => x !== null);
|
||||
|
||||
function isNonNull(x: number | null) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// factoring out a boolean works thanks to aliased discriminants
|
||||
function isNonNullVar(x: number | null) {
|
||||
const ok = x !== null;
|
||||
return ok;
|
||||
}
|
||||
|
||||
function isNonNullGeneric<T>(x: T) {
|
||||
return x !== null;
|
||||
}
|
||||
|
||||
// Type guards can flow between functions
|
||||
const myGuard = (o: string | undefined): o is string => !!o;
|
||||
const mySecondGuard = (o: string | undefined) => myGuard(o);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1327449914
|
||||
// This doesn't work because the false condition prevents type guard inference.
|
||||
// Breaking up the filters does work.
|
||||
type MyObj = { data?: string };
|
||||
type MyArray = { list?: MyObj[] }[];
|
||||
const myArray: MyArray = [];
|
||||
|
||||
const result = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => arr && arr.length)
|
||||
.map((arr) => arr // should error
|
||||
.filter((obj) => obj && obj.data)
|
||||
.map(obj => JSON.parse(obj.data)) // should error
|
||||
);
|
||||
|
||||
const result2 = myArray
|
||||
.map((arr) => arr.list)
|
||||
.filter((arr) => !!arr)
|
||||
.filter(arr => arr.length)
|
||||
.map((arr) => arr // should ok
|
||||
.filter((obj) => obj)
|
||||
// inferring a guard here would require https://github.com/microsoft/TypeScript/issues/42384
|
||||
.filter(obj => !!obj.data)
|
||||
.map(obj => JSON.parse(obj.data))
|
||||
);
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/16069#issuecomment-1183547889
|
||||
type Foo = {
|
||||
foo: string;
|
||||
}
|
||||
type Bar = Foo & {
|
||||
bar: string;
|
||||
}
|
||||
|
||||
const list: (Foo | Bar)[] = [];
|
||||
const resultBars: Bar[] = list.filter((value) => 'bar' in value); // should ok
|
||||
|
||||
function isBarNonNull(x: Foo | Bar | null) {
|
||||
return ('bar' in x!);
|
||||
}
|
||||
const fooOrBar = list[0];
|
||||
if (isBarNonNull(fooOrBar)) {
|
||||
const t: Bar = fooOrBar; // should ok
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/38390#issuecomment-626019466
|
||||
// Ryan's example (currently legal):
|
||||
const a = [1, "foo", 2, "bar"].filter(x => typeof x === "string");
|
||||
a.push(10);
|
||||
|
||||
// Defer to explicit type guards, even when they're incorrect.
|
||||
function backwardsGuard(x: number|string): x is number {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Partition tests. The "false" case matters.
|
||||
function isString(x: string | number) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let strOrNum: string | number;
|
||||
if (isString(strOrNum)) {
|
||||
let t: string = strOrNum; // should ok
|
||||
} else {
|
||||
let t: number = strOrNum; // should ok
|
||||
}
|
||||
|
||||
function flakyIsString(x: string | number) {
|
||||
return typeof x === 'string' && Math.random() > 0.5;
|
||||
}
|
||||
if (flakyIsString(strOrNum)) {
|
||||
let t: string = strOrNum; // should error
|
||||
} else {
|
||||
let t: number = strOrNum; // should error
|
||||
}
|
||||
|
||||
function isDate(x: object) {
|
||||
return x instanceof Date;
|
||||
}
|
||||
function flakyIsDate(x: object) {
|
||||
return x instanceof Date && Math.random() > 0.5;
|
||||
}
|
||||
|
||||
declare let maybeDate: object;
|
||||
if (isDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should ok
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
if (flakyIsDate(maybeDate)) {
|
||||
let t: Date = maybeDate; // should error
|
||||
} else {
|
||||
let t: object = maybeDate; // should ok
|
||||
}
|
||||
|
||||
// This should not infer a type guard since the value on which we do the refinement
|
||||
// is not related to the original parameter.
|
||||
function irrelevantIsNumber(x: string | number) {
|
||||
x = Math.random() < 0.5 ? "string" : 123;
|
||||
return typeof x === 'string';
|
||||
}
|
||||
function irrelevantIsNumberDestructuring(x: string | number) {
|
||||
[x] = [Math.random() < 0.5 ? "string" : 123];
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
// Cannot infer a type guard for either param because of the false case.
|
||||
function areBothNums(x: string|number, y: string|number) {
|
||||
return typeof x === 'number' && typeof y === 'number';
|
||||
}
|
||||
|
||||
// Could potentially infer a type guard here but it would require more bookkeeping.
|
||||
function doubleReturn(x: string|number) {
|
||||
if (typeof x === 'string') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function guardsOneButNotOthers(a: string|number, b: string|number, c: string|number) {
|
||||
return typeof b === 'string';
|
||||
}
|
||||
|
||||
// Checks that there are no string escaping issues
|
||||
function dunderguard(__x: number | string) {
|
||||
return typeof __x === 'string';
|
||||
}
|
||||
|
||||
// could infer a type guard here but it doesn't seem that helpful.
|
||||
const booleanIdentity = (x: boolean) => x;
|
||||
|
||||
// we infer "x is number | true" which is accurate but of debatable utility.
|
||||
const numOrBoolean = (x: number | boolean) => typeof x === 'number' || x;
|
||||
|
||||
// inferred guards in methods
|
||||
interface NumberInferrer {
|
||||
isNumber(x: number | string): x is number;
|
||||
}
|
||||
class Inferrer implements NumberInferrer {
|
||||
isNumber(x: number | string) { // should ok
|
||||
return typeof x === 'number';
|
||||
}
|
||||
}
|
||||
declare let numOrStr: number | string;
|
||||
const inf = new Inferrer();
|
||||
if (inf.isNumber(numOrStr)) {
|
||||
let t: number = numOrStr; // should ok
|
||||
} else {
|
||||
let t: string = numOrStr; // should ok
|
||||
}
|
||||
|
||||
// Type predicates are not inferred on "this"
|
||||
class C1 {
|
||||
isC2() {
|
||||
return this instanceof C2;
|
||||
}
|
||||
}
|
||||
class C2 extends C1 {
|
||||
z = 0;
|
||||
}
|
||||
declare let c: C1;
|
||||
if (c.isC2()) {
|
||||
let c2: C2 = c; // should error
|
||||
}
|
||||
|
||||
function doNotRefineDestructuredParam({x, y}: {x: number | null, y: number}) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
// The type predicate must remain valid when the function is called with subtypes.
|
||||
function isShortString(x: unknown) {
|
||||
return typeof x === "string" && x.length < 10;
|
||||
}
|
||||
|
||||
declare let str: string;
|
||||
if (isShortString(str)) {
|
||||
str.charAt(0); // should ok
|
||||
} else {
|
||||
str.charAt(0); // should ok
|
||||
}
|
||||
|
||||
function isStringFromUnknown(x: unknown) {
|
||||
return typeof x === "string";
|
||||
}
|
||||
if (isStringFromUnknown(str)) {
|
||||
str.charAt(0); // should OK
|
||||
} else {
|
||||
let t: never = str; // should OK
|
||||
}
|
||||
|
||||
// infer a union type
|
||||
function isNumOrStr(x: unknown) {
|
||||
return (typeof x === "number" || typeof x === "string");
|
||||
}
|
||||
declare let unk: unknown;
|
||||
if (isNumOrStr(unk)) {
|
||||
let t: number | string = unk; // should ok
|
||||
}
|
||||
|
||||
// A function can be a type predicate even if it throws.
|
||||
function assertAndPredicate(x: string | number | Date) {
|
||||
if (x instanceof Date) {
|
||||
throw new Error();
|
||||
}
|
||||
return typeof x === 'string';
|
||||
}
|
||||
|
||||
declare let snd: string | number | Date;
|
||||
if (assertAndPredicate(snd)) {
|
||||
let t: string = snd; // should error
|
||||
}
|
||||
|
||||
function isNumberWithThis(this: Date, x: number | string) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
function narrowFromAny(x: any) {
|
||||
return typeof x === 'number';
|
||||
}
|
||||
|
||||
const noInferenceFromRest = (...f: ["a" | "b"]) => f[0] === "a";
|
||||
const noInferenceFromImpossibleRest = (...f: []) => typeof f === "undefined";
|
||||
|
||||
function inferWithRest(x: string | null, ...f: ["a", "b"]) {
|
||||
return typeof x === 'string';
|
||||
}
|
||||
@ -65,5 +65,5 @@ verify.quickInfos({
|
||||
7: "(method) GuardInterface.isFollower(): this is FollowerGuard",
|
||||
13: "let leaderStatus: boolean",
|
||||
14: "let checkedLeaderStatus: boolean",
|
||||
15: "function isLeaderGuard(g: RoyalGuard): boolean"
|
||||
15: "function isLeaderGuard(g: RoyalGuard): g is LeadGuard"
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user