Improvements to strictSubtypeRelation and getNarrowedType (#52282)

This commit is contained in:
Anders Hejlsberg
2023-02-14 12:12:42 -08:00
committed by GitHub
parent a8b4a3b637
commit 84b8483206
17 changed files with 1819 additions and 39 deletions
+38 -21
View File
@@ -1252,12 +1252,13 @@ export const enum CheckMode {
/** @internal */
export const enum SignatureCheckMode {
None = 0,
None = 0,
BivariantCallback = 1 << 0,
StrictCallback = 1 << 1,
StrictCallback = 1 << 1,
IgnoreReturnTypes = 1 << 2,
StrictArity = 1 << 3,
Callback = BivariantCallback | StrictCallback,
StrictArity = 1 << 3,
StrictTopSignature = 1 << 4,
Callback = BivariantCallback | StrictCallback,
}
const enum IntersectionState {
@@ -19582,12 +19583,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
/**
* Returns true if `s` is `(...args: any[]) => any` or `(this: any, ...args: any[]) => any`
* Returns true if `s` is `(...args: A) => R` where `A` is `any`, `any[]`, `never`, or `never[]`, and `R` is `any` or `unknown`.
*/
function isAnySignature(s: Signature) {
return !s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 &&
signatureHasRestParameter(s) && (getTypeOfParameter(s.parameters[0]) === anyArrayType || isTypeAny(getTypeOfParameter(s.parameters[0]))) &&
isTypeAny(getReturnTypeOfSignature(s));
function isTopSignature(s: Signature) {
if (!s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 && signatureHasRestParameter(s)) {
const paramType = getTypeOfParameter(s.parameters[0]);
const restType = isArrayType(paramType) ? getTypeArguments(paramType)[0] : paramType;
return !!(restType.flags & (TypeFlags.Any | TypeFlags.Never) && getReturnTypeOfSignature(s).flags & TypeFlags.AnyOrUnknown);
}
return false;
}
/**
@@ -19606,9 +19610,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return Ternary.True;
}
if (isAnySignature(target)) {
if (!(checkMode & SignatureCheckMode.StrictTopSignature && isTopSignature(source)) && isTopSignature(target)) {
return Ternary.True;
}
if (checkMode & SignatureCheckMode.StrictTopSignature && isTopSignature(source) && !isTopSignature(target)) {
return Ternary.False;
}
const targetCount = getParameterCount(target);
const sourceHasMoreParameters = !hasEffectiveRestParameter(target) &&
@@ -19864,7 +19871,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map<string, RelationComparisonResult>, errorReporter?: ErrorReporter) {
const s = source.flags;
const t = target.flags;
if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true;
if (t & TypeFlags.Any || s & TypeFlags.Never || source === wildcardType) return true;
if (t & TypeFlags.Unknown && !(relation === strictSubtypeRelation && s & TypeFlags.Any)) return true;
if (t & TypeFlags.Never) return false;
if (s & TypeFlags.StringLike && t & TypeFlags.String) return true;
if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral &&
@@ -21486,8 +21494,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return Ternary.False;
}
}
// Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})`
// and not `{} <- fresh({}) <- {[idx: string]: any}`
// A fresh empty object type is never a subtype of a non-empty object type. This ensures fresh({}) <: { [x: string]: xxx }
// but not vice-versa. Without this rule, those types would be mutual subtypes.
else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
return Ternary.False;
}
@@ -22142,8 +22150,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
* See signatureAssignableTo, compareSignaturesIdentical
*/
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, intersectionState: IntersectionState, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
const checkMode = relation === subtypeRelation ? SignatureCheckMode.StrictTopSignature :
relation === strictSubtypeRelation ? SignatureCheckMode.StrictTopSignature | SignatureCheckMode.StrictArity :
SignatureCheckMode.None;
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
checkMode, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
function isRelatedToWorker(source: Type, target: Type, reportErrors?: boolean) {
return isRelatedTo(source, target, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
@@ -22239,8 +22250,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (sourceInfo) {
return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors, intersectionState);
}
if (!(intersectionState & IntersectionState.Source) && isObjectTypeWithInferableIndex(source)) {
// Intersection constituents are never considered to have an inferred index signature
// Intersection constituents are never considered to have an inferred index signature. Also, in the strict subtype relation,
// only fresh object literals are considered to have inferred index signatures. This ensures { [x: string]: xxx } <: {} but
// not vice-versa. Without this rule, those types would be mutual strict subtypes.
if (!(intersectionState & IntersectionState.Source) && (relation !== strictSubtypeRelation || getObjectFlags(source) & ObjectFlags.FreshLiteral) && isObjectTypeWithInferableIndex(source)) {
return membersRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState);
}
if (reportErrors) {
@@ -27089,21 +27102,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return emptyObjectType;
}
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean): Type {
const key = type.flags & TypeFlags.Union ? `N${getTypeId(type)},${getTypeId(candidate)},${(assumeTrue ? 1 : 0) | (checkDerived ? 2 : 0)}` : undefined;
return getCachedType(key) ?? setCachedType(key, getNarrowedTypeWorker(type, candidate, assumeTrue, checkDerived));
}
function getNarrowedTypeWorker(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
if (!assumeTrue) {
return filterType(type, t => !isRelated(t, candidate));
if (checkDerived) {
return filterType(type, t => !isTypeDerivedFrom(t, candidate));
}
const trueType = getNarrowedType(type, candidate, /*assumeTrue*/ true, /*checkDerived*/ false);
return filterType(type, t => !isTypeSubsetOf(t, trueType));
}
if (type.flags & TypeFlags.AnyOrUnknown) {
return candidate;
}
// We first attempt to filter the current type, narrowing constituents as appropriate and removing
// constituents that are unrelated to the candidate.
const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
const keyPropertyName = type.flags & TypeFlags.Union ? getKeyPropertyName(type as UnionType) : undefined;
const narrowedType = mapType(candidate, c => {
// If a discriminant property is available, use that to reduce the type.
@@ -27115,7 +27132,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// prototype object types.
const directlyRelated = mapType(matching || type, checkDerived ?
t => isTypeDerivedFrom(t, c) ? t : isTypeDerivedFrom(c, t) ? c : neverType :
t => isTypeSubtypeOf(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType);
t => isTypeSubtypeOf(c, t) && !isTypeIdenticalTo(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType);
// If no constituents are directly related, create intersections for any generic constituents that
// are related by constraint.
return directlyRelated.flags & TypeFlags.Never ?
@@ -36529,7 +36546,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
else {
checkAssignmentOperator(rightType);
return getRegularTypeOfObjectLiteral(rightType);
return rightType;
}
case SyntaxKind.CommaToken:
if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isIndirectCall(left.parent as BinaryExpression)) {
@@ -64,9 +64,9 @@ if (isNodeList(sourceObj)) {
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
sourceObj.length;
>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
}
if (isHTMLCollection(sourceObj)) {
@@ -74,9 +74,9 @@ if (isHTMLCollection(sourceObj)) {
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
sourceObj.length;
>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3))
>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33))
>length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33))
}
if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
@@ -69,18 +69,18 @@ if (isNodeList(sourceObj)) {
sourceObj.length;
>sourceObj.length : number
>sourceObj : NodeList
>sourceObj : NodeList | HTMLCollection
>length : number
}
if (isHTMLCollection(sourceObj)) {
>isHTMLCollection(sourceObj) : boolean
>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection
>sourceObj : NodeList | { a: string; }
>sourceObj : EventTargetLike
sourceObj.length;
>sourceObj.length : number
>sourceObj : HTMLCollection
>sourceObj : NodeList | HTMLCollection
>length : number
}
@@ -88,7 +88,7 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) {
>isNodeList(sourceObj) || isHTMLCollection(sourceObj) : boolean
>isNodeList(sourceObj) : boolean
>isNodeList : (sourceObj: any) => sourceObj is NodeList
>sourceObj : HTMLCollection | { a: string; }
>sourceObj : EventTargetLike
>isHTMLCollection(sourceObj) : boolean
>isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection
>sourceObj : { a: string; }
@@ -23,7 +23,7 @@ if (isObject1(obj1)) {
}
// check type after conditional block
obj1;
>obj1 : Record<string, unknown>
>obj1 : {}
declare const obj2: {} | undefined;
>obj2 : {} | undefined
@@ -43,7 +43,7 @@ if (isObject1(obj2)) {
}
// check type after conditional block
obj2;
>obj2 : Record<string, unknown> | undefined
>obj2 : {} | undefined
declare function isObject2(value: unknown): value is {};
>isObject2 : (value: unknown) => value is {}
@@ -67,7 +67,7 @@ if (isObject2(obj3)) {
}
// check type after conditional block
obj3;
>obj3 : {}
>obj3 : Record<string, unknown>
declare const obj4: Record<string, unknown> | undefined;
>obj4 : Record<string, unknown> | undefined
@@ -87,5 +87,5 @@ if (isObject2(obj4)) {
}
// check type after conditional block
obj4;
>obj4 : {} | undefined
>obj4 : Record<string, unknown> | undefined
@@ -95,9 +95,9 @@ function foo2(x: C1 | C2 | C3): string {
>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14))
return x.item;
>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10))
>x.item : Symbol(item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10), Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10))
>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14))
>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10))
>item : Symbol(item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10), Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10))
}
else if (isC2(x)) {
>isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66))
@@ -85,7 +85,7 @@ function foo2(x: C1 | C2 | C3): string {
return x.item;
>x.item : string
>x : C1
>x : C1 | C3
>item : string
}
else if (isC2(x)) {
@@ -0,0 +1,126 @@
//// [narrowingMutualSubtypes.ts]
// Check that `any` is a strict supertype of `unknown`
declare const ru1: { [x: string]: unknown };
declare const ra1: { [x: string]: any };
const a1a = [ru1, ra1]; // { [x: string]: any }[]
const a1b = [ra1, ru1]; // { [x: string]: any }[]
declare const ra2: { [x: string]: any };
declare const ru2: { [x: string]: unknown };
const a2a = [ru2, ra2]; // { [x: string]: any }[]
const a2b = [ra2, ru2]; // { [x: string]: any }[]
// Check that `{}` is strict supertype of any non-empty object
const c3 = {};
declare const r3: { [x: string]: unknown }
const a3a = [c3, r3]; // {}[]
const a3b = [r3, c3]; // {}[]
declare const r4: { [x: string]: unknown }
const c4 = {};
const a4a = [c4, r4]; // {}[]
const a4b = [r4, c4]; // {}[]
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
declare function isObject1(value: unknown): value is Record<string, unknown>;
function gg(x: {}) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
else {
x; // {}
}
x; // {}
}
declare function isObject2(value: unknown): value is {};
function gg2(x: Record<string, unknown>) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
x; // Record<string, unknown>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
type Self<T> = T extends unknown ? Identity<T> : never;
function is<T>(value: T): value is Self<T> {
return true;
}
type Union = {a: number} | {b: number} | {c: number};
function example(x: Union) {
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
x; // Union
}
//// [narrowingMutualSubtypes.js]
"use strict";
// Check that `any` is a strict supertype of `unknown`
var a1a = [ru1, ra1]; // { [x: string]: any }[]
var a1b = [ra1, ru1]; // { [x: string]: any }[]
var a2a = [ru2, ra2]; // { [x: string]: any }[]
var a2b = [ra2, ru2]; // { [x: string]: any }[]
// Check that `{}` is strict supertype of any non-empty object
var c3 = {};
var a3a = [c3, r3]; // {}[]
var a3b = [r3, c3]; // {}[]
var c4 = {};
var a4a = [c4, r4]; // {}[]
var a4b = [r4, c4]; // {}[]
function gg(x) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
else {
x; // {}
}
x; // {}
}
function gg2(x) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
x; // Record<string, unknown>
}
function is(value) {
return true;
}
function example(x) {
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
if (is(x)) { }
x; // Union
}
@@ -0,0 +1,203 @@
=== tests/cases/compiler/narrowingMutualSubtypes.ts ===
// Check that `any` is a strict supertype of `unknown`
declare const ru1: { [x: string]: unknown };
>ru1 : Symbol(ru1, Decl(narrowingMutualSubtypes.ts, 2, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 2, 22))
declare const ra1: { [x: string]: any };
>ra1 : Symbol(ra1, Decl(narrowingMutualSubtypes.ts, 3, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 3, 22))
const a1a = [ru1, ra1]; // { [x: string]: any }[]
>a1a : Symbol(a1a, Decl(narrowingMutualSubtypes.ts, 5, 5))
>ru1 : Symbol(ru1, Decl(narrowingMutualSubtypes.ts, 2, 13))
>ra1 : Symbol(ra1, Decl(narrowingMutualSubtypes.ts, 3, 13))
const a1b = [ra1, ru1]; // { [x: string]: any }[]
>a1b : Symbol(a1b, Decl(narrowingMutualSubtypes.ts, 6, 5))
>ra1 : Symbol(ra1, Decl(narrowingMutualSubtypes.ts, 3, 13))
>ru1 : Symbol(ru1, Decl(narrowingMutualSubtypes.ts, 2, 13))
declare const ra2: { [x: string]: any };
>ra2 : Symbol(ra2, Decl(narrowingMutualSubtypes.ts, 8, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 8, 22))
declare const ru2: { [x: string]: unknown };
>ru2 : Symbol(ru2, Decl(narrowingMutualSubtypes.ts, 9, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 9, 22))
const a2a = [ru2, ra2]; // { [x: string]: any }[]
>a2a : Symbol(a2a, Decl(narrowingMutualSubtypes.ts, 11, 5))
>ru2 : Symbol(ru2, Decl(narrowingMutualSubtypes.ts, 9, 13))
>ra2 : Symbol(ra2, Decl(narrowingMutualSubtypes.ts, 8, 13))
const a2b = [ra2, ru2]; // { [x: string]: any }[]
>a2b : Symbol(a2b, Decl(narrowingMutualSubtypes.ts, 12, 5))
>ra2 : Symbol(ra2, Decl(narrowingMutualSubtypes.ts, 8, 13))
>ru2 : Symbol(ru2, Decl(narrowingMutualSubtypes.ts, 9, 13))
// Check that `{}` is strict supertype of any non-empty object
const c3 = {};
>c3 : Symbol(c3, Decl(narrowingMutualSubtypes.ts, 16, 5))
declare const r3: { [x: string]: unknown }
>r3 : Symbol(r3, Decl(narrowingMutualSubtypes.ts, 17, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 17, 21))
const a3a = [c3, r3]; // {}[]
>a3a : Symbol(a3a, Decl(narrowingMutualSubtypes.ts, 19, 5))
>c3 : Symbol(c3, Decl(narrowingMutualSubtypes.ts, 16, 5))
>r3 : Symbol(r3, Decl(narrowingMutualSubtypes.ts, 17, 13))
const a3b = [r3, c3]; // {}[]
>a3b : Symbol(a3b, Decl(narrowingMutualSubtypes.ts, 20, 5))
>r3 : Symbol(r3, Decl(narrowingMutualSubtypes.ts, 17, 13))
>c3 : Symbol(c3, Decl(narrowingMutualSubtypes.ts, 16, 5))
declare const r4: { [x: string]: unknown }
>r4 : Symbol(r4, Decl(narrowingMutualSubtypes.ts, 22, 13))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 22, 21))
const c4 = {};
>c4 : Symbol(c4, Decl(narrowingMutualSubtypes.ts, 23, 5))
const a4a = [c4, r4]; // {}[]
>a4a : Symbol(a4a, Decl(narrowingMutualSubtypes.ts, 25, 5))
>c4 : Symbol(c4, Decl(narrowingMutualSubtypes.ts, 23, 5))
>r4 : Symbol(r4, Decl(narrowingMutualSubtypes.ts, 22, 13))
const a4b = [r4, c4]; // {}[]
>a4b : Symbol(a4b, Decl(narrowingMutualSubtypes.ts, 26, 5))
>r4 : Symbol(r4, Decl(narrowingMutualSubtypes.ts, 22, 13))
>c4 : Symbol(c4, Decl(narrowingMutualSubtypes.ts, 23, 5))
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
declare function isObject1(value: unknown): value is Record<string, unknown>;
>isObject1 : Symbol(isObject1, Decl(narrowingMutualSubtypes.ts, 26, 21))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 30, 27))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 30, 27))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
function gg(x: {}) {
>gg : Symbol(gg, Decl(narrowingMutualSubtypes.ts, 30, 77))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
if (isObject1(x)) {
>isObject1 : Symbol(isObject1, Decl(narrowingMutualSubtypes.ts, 26, 21))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
}
else {
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
}
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
}
declare function isObject2(value: unknown): value is {};
>isObject2 : Symbol(isObject2, Decl(narrowingMutualSubtypes.ts, 40, 1))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 42, 27))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 42, 27))
function gg2(x: Record<string, unknown>) {
>gg2 : Symbol(gg2, Decl(narrowingMutualSubtypes.ts, 42, 56))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
if (isObject2(x)) {
>isObject2 : Symbol(isObject2, Decl(narrowingMutualSubtypes.ts, 40, 1))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
else {
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 52, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 56, 21))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 56, 21))
type Self<T> = T extends unknown ? Identity<T> : never;
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 56, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 52, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
function is<T>(value: T): value is Self<T> {
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 60, 15))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 60, 15))
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 56, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
return true;
}
type Union = {a: number} | {b: number} | {c: number};
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 62, 1))
>a : Symbol(a, Decl(narrowingMutualSubtypes.ts, 64, 15))
>b : Symbol(b, Decl(narrowingMutualSubtypes.ts, 64, 29))
>c : Symbol(c, Decl(narrowingMutualSubtypes.ts, 64, 43))
function example(x: Union) {
>example : Symbol(example, Decl(narrowingMutualSubtypes.ts, 64, 54))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 62, 1))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
x; // Union
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
}
@@ -0,0 +1,205 @@
=== tests/cases/compiler/narrowingMutualSubtypes.ts ===
// Check that `any` is a strict supertype of `unknown`
declare const ru1: { [x: string]: unknown };
>ru1 : { [x: string]: unknown; }
>x : string
declare const ra1: { [x: string]: any };
>ra1 : { [x: string]: any; }
>x : string
const a1a = [ru1, ra1]; // { [x: string]: any }[]
>a1a : { [x: string]: any; }[]
>[ru1, ra1] : { [x: string]: any; }[]
>ru1 : { [x: string]: unknown; }
>ra1 : { [x: string]: any; }
const a1b = [ra1, ru1]; // { [x: string]: any }[]
>a1b : { [x: string]: any; }[]
>[ra1, ru1] : { [x: string]: any; }[]
>ra1 : { [x: string]: any; }
>ru1 : { [x: string]: unknown; }
declare const ra2: { [x: string]: any };
>ra2 : { [x: string]: any; }
>x : string
declare const ru2: { [x: string]: unknown };
>ru2 : { [x: string]: unknown; }
>x : string
const a2a = [ru2, ra2]; // { [x: string]: any }[]
>a2a : { [x: string]: any; }[]
>[ru2, ra2] : { [x: string]: any; }[]
>ru2 : { [x: string]: unknown; }
>ra2 : { [x: string]: any; }
const a2b = [ra2, ru2]; // { [x: string]: any }[]
>a2b : { [x: string]: any; }[]
>[ra2, ru2] : { [x: string]: any; }[]
>ra2 : { [x: string]: any; }
>ru2 : { [x: string]: unknown; }
// Check that `{}` is strict supertype of any non-empty object
const c3 = {};
>c3 : {}
>{} : {}
declare const r3: { [x: string]: unknown }
>r3 : { [x: string]: unknown; }
>x : string
const a3a = [c3, r3]; // {}[]
>a3a : {}[]
>[c3, r3] : {}[]
>c3 : {}
>r3 : { [x: string]: unknown; }
const a3b = [r3, c3]; // {}[]
>a3b : {}[]
>[r3, c3] : {}[]
>r3 : { [x: string]: unknown; }
>c3 : {}
declare const r4: { [x: string]: unknown }
>r4 : { [x: string]: unknown; }
>x : string
const c4 = {};
>c4 : {}
>{} : {}
const a4a = [c4, r4]; // {}[]
>a4a : {}[]
>[c4, r4] : {}[]
>c4 : {}
>r4 : { [x: string]: unknown; }
const a4b = [r4, c4]; // {}[]
>a4b : {}[]
>[r4, c4] : {}[]
>r4 : { [x: string]: unknown; }
>c4 : {}
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
declare function isObject1(value: unknown): value is Record<string, unknown>;
>isObject1 : (value: unknown) => value is Record<string, unknown>
>value : unknown
function gg(x: {}) {
>gg : (x: {}) => void
>x : {}
if (isObject1(x)) {
>isObject1(x) : boolean
>isObject1 : (value: unknown) => value is Record<string, unknown>
>x : {}
x; // Record<string, unknown>
>x : Record<string, unknown>
}
else {
x; // {}
>x : {}
}
x; // {}
>x : {}
}
declare function isObject2(value: unknown): value is {};
>isObject2 : (value: unknown) => value is {}
>value : unknown
function gg2(x: Record<string, unknown>) {
>gg2 : (x: Record<string, unknown>) => void
>x : Record<string, unknown>
if (isObject2(x)) {
>isObject2(x) : boolean
>isObject2 : (value: unknown) => value is {}
>x : Record<string, unknown>
x; // {}
>x : {}
}
else {
x; // Record<string, unknown>
>x : Record<string, unknown>
}
x; // Record<string, unknown>
>x : Record<string, unknown>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
>Identity : Identity<T>
type Self<T> = T extends unknown ? Identity<T> : never;
>Self : Self<T>
function is<T>(value: T): value is Self<T> {
>is : <T>(value: T) => value is Self<T>
>value : T
return true;
>true : true
}
type Union = {a: number} | {b: number} | {c: number};
>Union : { a: number; } | { b: number; } | { c: number; }
>a : number
>b : number
>c : number
function example(x: Union) {
>example : (x: Union) => void
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
if (is(x)) {}
>is(x) : boolean
>is : <T>(value: T) => value is Self<T>
>x : Union
x; // Union
>x : Union
}
@@ -0,0 +1,149 @@
tests/cases/compiler/strictSubtypeAndNarrowing.ts(123,27): error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
tests/cases/compiler/strictSubtypeAndNarrowing.ts(124,27): error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
tests/cases/compiler/strictSubtypeAndNarrowing.ts(129,26): error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
==== tests/cases/compiler/strictSubtypeAndNarrowing.ts (3 errors) ====
// Check that `any` is a strict supertype of `unknown`
declare const x11: { x: unknown };
declare const x12: { x: any };
const a11 = [x11, x12];
const a12 = [x12, x11];
declare const x21: { x: any };
declare const x22: { x: unknown };
const a21 = [x22, x21];
const a22 = [x21, x22];
// Strict subtype doesn't infer index signatures in non-fresh object types
const x31 = { a: 1 };
declare const x32: { [x: string]: unknown, a: number }
const a31 = [x31, x32];
const a32 = [x32, x31];
declare const x41: { [x: string]: unknown, a: number }
const x42 = { a: 1 };
const a41 = [x42, x41];
const a42 = [x41, x42];
// (...args: A) => R, where A is any, any[], never, or never[] and R is any or unknown, is supertype of all function types.
declare function isFunction<T>(x: unknown): x is T;
type A = (...args: any) => unknown;
type B = (...args: any[]) => unknown;
type C = (...args: never) => unknown;
type D = (...args: never[]) => unknown;
type FnTypes = A | B | C | D;
function fx1(f: (() => void) | undefined) {
if (isFunction<A>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx2(f: (() => void) | undefined) {
if (isFunction<B>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx3(f: (() => void) | undefined) {
if (isFunction<C>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx4(f: (() => void) | undefined) {
if (isFunction<D>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function checkA(f: FnTypes) {
if (isFunction<A>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkB(f: FnTypes) {
if (isFunction<B>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkC(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
function checkD(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
// Type of x = y is y with freshness preserved
function fx10(obj1: { x?: number }, obj2: { x?: number, y?: number }) {
obj1 = obj2 = { x: 1, y: 2 };
~~~~
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
obj2 = obj1 = { x: 1, y: 2 };
~~~~
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
}
function fx11(): { x?: number } {
let obj: { x?: number, y?: number };
return obj = { x: 1, y: 2 };
~~~~
!!! error TS2322: Type '{ x: number; y: number; }' is not assignable to type '{ x?: number | undefined; }'.
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type '{ x?: number | undefined; }'.
}
@@ -0,0 +1,228 @@
//// [strictSubtypeAndNarrowing.ts]
// Check that `any` is a strict supertype of `unknown`
declare const x11: { x: unknown };
declare const x12: { x: any };
const a11 = [x11, x12];
const a12 = [x12, x11];
declare const x21: { x: any };
declare const x22: { x: unknown };
const a21 = [x22, x21];
const a22 = [x21, x22];
// Strict subtype doesn't infer index signatures in non-fresh object types
const x31 = { a: 1 };
declare const x32: { [x: string]: unknown, a: number }
const a31 = [x31, x32];
const a32 = [x32, x31];
declare const x41: { [x: string]: unknown, a: number }
const x42 = { a: 1 };
const a41 = [x42, x41];
const a42 = [x41, x42];
// (...args: A) => R, where A is any, any[], never, or never[] and R is any or unknown, is supertype of all function types.
declare function isFunction<T>(x: unknown): x is T;
type A = (...args: any) => unknown;
type B = (...args: any[]) => unknown;
type C = (...args: never) => unknown;
type D = (...args: never[]) => unknown;
type FnTypes = A | B | C | D;
function fx1(f: (() => void) | undefined) {
if (isFunction<A>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx2(f: (() => void) | undefined) {
if (isFunction<B>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx3(f: (() => void) | undefined) {
if (isFunction<C>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx4(f: (() => void) | undefined) {
if (isFunction<D>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function checkA(f: FnTypes) {
if (isFunction<A>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkB(f: FnTypes) {
if (isFunction<B>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkC(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
function checkD(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
// Type of x = y is y with freshness preserved
function fx10(obj1: { x?: number }, obj2: { x?: number, y?: number }) {
obj1 = obj2 = { x: 1, y: 2 };
obj2 = obj1 = { x: 1, y: 2 };
}
function fx11(): { x?: number } {
let obj: { x?: number, y?: number };
return obj = { x: 1, y: 2 };
}
//// [strictSubtypeAndNarrowing.js]
"use strict";
// Check that `any` is a strict supertype of `unknown`
var a11 = [x11, x12];
var a12 = [x12, x11];
var a21 = [x22, x21];
var a22 = [x21, x22];
// Strict subtype doesn't infer index signatures in non-fresh object types
var x31 = { a: 1 };
var a31 = [x31, x32];
var a32 = [x32, x31];
var x42 = { a: 1 };
var a41 = [x42, x41];
var a42 = [x41, x42];
function fx1(f) {
if (isFunction(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx2(f) {
if (isFunction(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx3(f) {
if (isFunction(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx4(f) {
if (isFunction(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function checkA(f) {
if (isFunction(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkB(f) {
if (isFunction(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkC(f) {
if (isFunction(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
function checkD(f) {
if (isFunction(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
// Type of x = y is y with freshness preserved
function fx10(obj1, obj2) {
obj1 = obj2 = { x: 1, y: 2 };
obj2 = obj1 = { x: 1, y: 2 };
}
function fx11() {
var obj;
return obj = { x: 1, y: 2 };
}
@@ -0,0 +1,313 @@
=== tests/cases/compiler/strictSubtypeAndNarrowing.ts ===
// Check that `any` is a strict supertype of `unknown`
declare const x11: { x: unknown };
>x11 : Symbol(x11, Decl(strictSubtypeAndNarrowing.ts, 2, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 2, 20))
declare const x12: { x: any };
>x12 : Symbol(x12, Decl(strictSubtypeAndNarrowing.ts, 3, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 3, 20))
const a11 = [x11, x12];
>a11 : Symbol(a11, Decl(strictSubtypeAndNarrowing.ts, 5, 5))
>x11 : Symbol(x11, Decl(strictSubtypeAndNarrowing.ts, 2, 13))
>x12 : Symbol(x12, Decl(strictSubtypeAndNarrowing.ts, 3, 13))
const a12 = [x12, x11];
>a12 : Symbol(a12, Decl(strictSubtypeAndNarrowing.ts, 6, 5))
>x12 : Symbol(x12, Decl(strictSubtypeAndNarrowing.ts, 3, 13))
>x11 : Symbol(x11, Decl(strictSubtypeAndNarrowing.ts, 2, 13))
declare const x21: { x: any };
>x21 : Symbol(x21, Decl(strictSubtypeAndNarrowing.ts, 8, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 8, 20))
declare const x22: { x: unknown };
>x22 : Symbol(x22, Decl(strictSubtypeAndNarrowing.ts, 9, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 9, 20))
const a21 = [x22, x21];
>a21 : Symbol(a21, Decl(strictSubtypeAndNarrowing.ts, 11, 5))
>x22 : Symbol(x22, Decl(strictSubtypeAndNarrowing.ts, 9, 13))
>x21 : Symbol(x21, Decl(strictSubtypeAndNarrowing.ts, 8, 13))
const a22 = [x21, x22];
>a22 : Symbol(a22, Decl(strictSubtypeAndNarrowing.ts, 12, 5))
>x21 : Symbol(x21, Decl(strictSubtypeAndNarrowing.ts, 8, 13))
>x22 : Symbol(x22, Decl(strictSubtypeAndNarrowing.ts, 9, 13))
// Strict subtype doesn't infer index signatures in non-fresh object types
const x31 = { a: 1 };
>x31 : Symbol(x31, Decl(strictSubtypeAndNarrowing.ts, 16, 5))
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 16, 13))
declare const x32: { [x: string]: unknown, a: number }
>x32 : Symbol(x32, Decl(strictSubtypeAndNarrowing.ts, 17, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 17, 22))
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 17, 42))
const a31 = [x31, x32];
>a31 : Symbol(a31, Decl(strictSubtypeAndNarrowing.ts, 19, 5))
>x31 : Symbol(x31, Decl(strictSubtypeAndNarrowing.ts, 16, 5))
>x32 : Symbol(x32, Decl(strictSubtypeAndNarrowing.ts, 17, 13))
const a32 = [x32, x31];
>a32 : Symbol(a32, Decl(strictSubtypeAndNarrowing.ts, 20, 5))
>x32 : Symbol(x32, Decl(strictSubtypeAndNarrowing.ts, 17, 13))
>x31 : Symbol(x31, Decl(strictSubtypeAndNarrowing.ts, 16, 5))
declare const x41: { [x: string]: unknown, a: number }
>x41 : Symbol(x41, Decl(strictSubtypeAndNarrowing.ts, 22, 13))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 22, 22))
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 22, 42))
const x42 = { a: 1 };
>x42 : Symbol(x42, Decl(strictSubtypeAndNarrowing.ts, 23, 5))
>a : Symbol(a, Decl(strictSubtypeAndNarrowing.ts, 23, 13))
const a41 = [x42, x41];
>a41 : Symbol(a41, Decl(strictSubtypeAndNarrowing.ts, 25, 5))
>x42 : Symbol(x42, Decl(strictSubtypeAndNarrowing.ts, 23, 5))
>x41 : Symbol(x41, Decl(strictSubtypeAndNarrowing.ts, 22, 13))
const a42 = [x41, x42];
>a42 : Symbol(a42, Decl(strictSubtypeAndNarrowing.ts, 26, 5))
>x41 : Symbol(x41, Decl(strictSubtypeAndNarrowing.ts, 22, 13))
>x42 : Symbol(x42, Decl(strictSubtypeAndNarrowing.ts, 23, 5))
// (...args: A) => R, where A is any, any[], never, or never[] and R is any or unknown, is supertype of all function types.
declare function isFunction<T>(x: unknown): x is T;
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>T : Symbol(T, Decl(strictSubtypeAndNarrowing.ts, 30, 28))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 30, 31))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 30, 31))
>T : Symbol(T, Decl(strictSubtypeAndNarrowing.ts, 30, 28))
type A = (...args: any) => unknown;
>A : Symbol(A, Decl(strictSubtypeAndNarrowing.ts, 30, 51))
>args : Symbol(args, Decl(strictSubtypeAndNarrowing.ts, 32, 10))
type B = (...args: any[]) => unknown;
>B : Symbol(B, Decl(strictSubtypeAndNarrowing.ts, 32, 35))
>args : Symbol(args, Decl(strictSubtypeAndNarrowing.ts, 33, 10))
type C = (...args: never) => unknown;
>C : Symbol(C, Decl(strictSubtypeAndNarrowing.ts, 33, 37))
>args : Symbol(args, Decl(strictSubtypeAndNarrowing.ts, 34, 10))
type D = (...args: never[]) => unknown;
>D : Symbol(D, Decl(strictSubtypeAndNarrowing.ts, 34, 37))
>args : Symbol(args, Decl(strictSubtypeAndNarrowing.ts, 35, 10))
type FnTypes = A | B | C | D;
>FnTypes : Symbol(FnTypes, Decl(strictSubtypeAndNarrowing.ts, 35, 39))
>A : Symbol(A, Decl(strictSubtypeAndNarrowing.ts, 30, 51))
>B : Symbol(B, Decl(strictSubtypeAndNarrowing.ts, 32, 35))
>C : Symbol(C, Decl(strictSubtypeAndNarrowing.ts, 33, 37))
>D : Symbol(D, Decl(strictSubtypeAndNarrowing.ts, 34, 37))
function fx1(f: (() => void) | undefined) {
>fx1 : Symbol(fx1, Decl(strictSubtypeAndNarrowing.ts, 37, 29))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 39, 13))
if (isFunction<A>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>A : Symbol(A, Decl(strictSubtypeAndNarrowing.ts, 30, 51))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 39, 13))
f; // () => void
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 39, 13))
}
else {
f; // undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 39, 13))
}
f; // (() => void) | undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 39, 13))
}
function fx2(f: (() => void) | undefined) {
>fx2 : Symbol(fx2, Decl(strictSubtypeAndNarrowing.ts, 47, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 49, 13))
if (isFunction<B>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>B : Symbol(B, Decl(strictSubtypeAndNarrowing.ts, 32, 35))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 49, 13))
f; // () => void
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 49, 13))
}
else {
f; // undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 49, 13))
}
f; // (() => void) | undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 49, 13))
}
function fx3(f: (() => void) | undefined) {
>fx3 : Symbol(fx3, Decl(strictSubtypeAndNarrowing.ts, 57, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 59, 13))
if (isFunction<C>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>C : Symbol(C, Decl(strictSubtypeAndNarrowing.ts, 33, 37))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 59, 13))
f; // () => void
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 59, 13))
}
else {
f; // undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 59, 13))
}
f; // (() => void) | undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 59, 13))
}
function fx4(f: (() => void) | undefined) {
>fx4 : Symbol(fx4, Decl(strictSubtypeAndNarrowing.ts, 67, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 69, 13))
if (isFunction<D>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>D : Symbol(D, Decl(strictSubtypeAndNarrowing.ts, 34, 37))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 69, 13))
f; // () => void
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 69, 13))
}
else {
f; // undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 69, 13))
}
f; // (() => void) | undefined
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 69, 13))
}
function checkA(f: FnTypes) {
>checkA : Symbol(checkA, Decl(strictSubtypeAndNarrowing.ts, 77, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 79, 16))
>FnTypes : Symbol(FnTypes, Decl(strictSubtypeAndNarrowing.ts, 35, 39))
if (isFunction<A>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>A : Symbol(A, Decl(strictSubtypeAndNarrowing.ts, 30, 51))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 79, 16))
f; // A | B
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 79, 16))
}
else {
f; // C | D
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 79, 16))
}
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 79, 16))
}
function checkB(f: FnTypes) {
>checkB : Symbol(checkB, Decl(strictSubtypeAndNarrowing.ts, 87, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 89, 16))
>FnTypes : Symbol(FnTypes, Decl(strictSubtypeAndNarrowing.ts, 35, 39))
if (isFunction<B>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>B : Symbol(B, Decl(strictSubtypeAndNarrowing.ts, 32, 35))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 89, 16))
f; // A | B
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 89, 16))
}
else {
f; // C | D
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 89, 16))
}
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 89, 16))
}
function checkC(f: FnTypes) {
>checkC : Symbol(checkC, Decl(strictSubtypeAndNarrowing.ts, 97, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 99, 16))
>FnTypes : Symbol(FnTypes, Decl(strictSubtypeAndNarrowing.ts, 35, 39))
if (isFunction<C>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>C : Symbol(C, Decl(strictSubtypeAndNarrowing.ts, 33, 37))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 99, 16))
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 99, 16))
}
else {
f; // never
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 99, 16))
}
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 99, 16))
}
function checkD(f: FnTypes) {
>checkD : Symbol(checkD, Decl(strictSubtypeAndNarrowing.ts, 107, 1))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 109, 16))
>FnTypes : Symbol(FnTypes, Decl(strictSubtypeAndNarrowing.ts, 35, 39))
if (isFunction<C>(f)) {
>isFunction : Symbol(isFunction, Decl(strictSubtypeAndNarrowing.ts, 26, 23))
>C : Symbol(C, Decl(strictSubtypeAndNarrowing.ts, 33, 37))
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 109, 16))
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 109, 16))
}
else {
f; // never
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 109, 16))
}
f; // FnTypes
>f : Symbol(f, Decl(strictSubtypeAndNarrowing.ts, 109, 16))
}
// Type of x = y is y with freshness preserved
function fx10(obj1: { x?: number }, obj2: { x?: number, y?: number }) {
>fx10 : Symbol(fx10, Decl(strictSubtypeAndNarrowing.ts, 117, 1))
>obj1 : Symbol(obj1, Decl(strictSubtypeAndNarrowing.ts, 121, 14))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 121, 21))
>obj2 : Symbol(obj2, Decl(strictSubtypeAndNarrowing.ts, 121, 35))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 121, 43))
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 121, 55))
obj1 = obj2 = { x: 1, y: 2 };
>obj1 : Symbol(obj1, Decl(strictSubtypeAndNarrowing.ts, 121, 14))
>obj2 : Symbol(obj2, Decl(strictSubtypeAndNarrowing.ts, 121, 35))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 122, 19))
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 122, 25))
obj2 = obj1 = { x: 1, y: 2 };
>obj2 : Symbol(obj2, Decl(strictSubtypeAndNarrowing.ts, 121, 35))
>obj1 : Symbol(obj1, Decl(strictSubtypeAndNarrowing.ts, 121, 14))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 123, 19))
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 123, 25))
}
function fx11(): { x?: number } {
>fx11 : Symbol(fx11, Decl(strictSubtypeAndNarrowing.ts, 124, 1))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 126, 18))
let obj: { x?: number, y?: number };
>obj : Symbol(obj, Decl(strictSubtypeAndNarrowing.ts, 127, 7))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 127, 14))
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 127, 26))
return obj = { x: 1, y: 2 };
>obj : Symbol(obj, Decl(strictSubtypeAndNarrowing.ts, 127, 7))
>x : Symbol(x, Decl(strictSubtypeAndNarrowing.ts, 128, 18))
>y : Symbol(y, Decl(strictSubtypeAndNarrowing.ts, 128, 24))
}
@@ -0,0 +1,328 @@
=== tests/cases/compiler/strictSubtypeAndNarrowing.ts ===
// Check that `any` is a strict supertype of `unknown`
declare const x11: { x: unknown };
>x11 : { x: unknown; }
>x : unknown
declare const x12: { x: any };
>x12 : { x: any; }
>x : any
const a11 = [x11, x12];
>a11 : { x: any; }[]
>[x11, x12] : { x: any; }[]
>x11 : { x: unknown; }
>x12 : { x: any; }
const a12 = [x12, x11];
>a12 : { x: any; }[]
>[x12, x11] : { x: any; }[]
>x12 : { x: any; }
>x11 : { x: unknown; }
declare const x21: { x: any };
>x21 : { x: any; }
>x : any
declare const x22: { x: unknown };
>x22 : { x: unknown; }
>x : unknown
const a21 = [x22, x21];
>a21 : { x: any; }[]
>[x22, x21] : { x: any; }[]
>x22 : { x: unknown; }
>x21 : { x: any; }
const a22 = [x21, x22];
>a22 : { x: any; }[]
>[x21, x22] : { x: any; }[]
>x21 : { x: any; }
>x22 : { x: unknown; }
// Strict subtype doesn't infer index signatures in non-fresh object types
const x31 = { a: 1 };
>x31 : { a: number; }
>{ a: 1 } : { a: number; }
>a : number
>1 : 1
declare const x32: { [x: string]: unknown, a: number }
>x32 : { [x: string]: unknown; a: number; }
>x : string
>a : number
const a31 = [x31, x32];
>a31 : { a: number; }[]
>[x31, x32] : { a: number; }[]
>x31 : { a: number; }
>x32 : { [x: string]: unknown; a: number; }
const a32 = [x32, x31];
>a32 : { a: number; }[]
>[x32, x31] : { a: number; }[]
>x32 : { [x: string]: unknown; a: number; }
>x31 : { a: number; }
declare const x41: { [x: string]: unknown, a: number }
>x41 : { [x: string]: unknown; a: number; }
>x : string
>a : number
const x42 = { a: 1 };
>x42 : { a: number; }
>{ a: 1 } : { a: number; }
>a : number
>1 : 1
const a41 = [x42, x41];
>a41 : { a: number; }[]
>[x42, x41] : { a: number; }[]
>x42 : { a: number; }
>x41 : { [x: string]: unknown; a: number; }
const a42 = [x41, x42];
>a42 : { a: number; }[]
>[x41, x42] : { a: number; }[]
>x41 : { [x: string]: unknown; a: number; }
>x42 : { a: number; }
// (...args: A) => R, where A is any, any[], never, or never[] and R is any or unknown, is supertype of all function types.
declare function isFunction<T>(x: unknown): x is T;
>isFunction : <T>(x: unknown) => x is T
>x : unknown
type A = (...args: any) => unknown;
>A : (...args: any) => unknown
>args : any
type B = (...args: any[]) => unknown;
>B : (...args: any[]) => unknown
>args : any[]
type C = (...args: never) => unknown;
>C : (...args: never) => unknown
>args : never
type D = (...args: never[]) => unknown;
>D : (...args: never[]) => unknown
>args : never[]
type FnTypes = A | B | C | D;
>FnTypes : A | B | C | D
function fx1(f: (() => void) | undefined) {
>fx1 : (f: (() => void) | undefined) => void
>f : (() => void) | undefined
if (isFunction<A>(f)) {
>isFunction<A>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : (() => void) | undefined
f; // () => void
>f : () => void
}
else {
f; // undefined
>f : undefined
}
f; // (() => void) | undefined
>f : (() => void) | undefined
}
function fx2(f: (() => void) | undefined) {
>fx2 : (f: (() => void) | undefined) => void
>f : (() => void) | undefined
if (isFunction<B>(f)) {
>isFunction<B>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : (() => void) | undefined
f; // () => void
>f : () => void
}
else {
f; // undefined
>f : undefined
}
f; // (() => void) | undefined
>f : (() => void) | undefined
}
function fx3(f: (() => void) | undefined) {
>fx3 : (f: (() => void) | undefined) => void
>f : (() => void) | undefined
if (isFunction<C>(f)) {
>isFunction<C>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : (() => void) | undefined
f; // () => void
>f : () => void
}
else {
f; // undefined
>f : undefined
}
f; // (() => void) | undefined
>f : (() => void) | undefined
}
function fx4(f: (() => void) | undefined) {
>fx4 : (f: (() => void) | undefined) => void
>f : (() => void) | undefined
if (isFunction<D>(f)) {
>isFunction<D>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : (() => void) | undefined
f; // () => void
>f : () => void
}
else {
f; // undefined
>f : undefined
}
f; // (() => void) | undefined
>f : (() => void) | undefined
}
function checkA(f: FnTypes) {
>checkA : (f: FnTypes) => void
>f : FnTypes
if (isFunction<A>(f)) {
>isFunction<A>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : FnTypes
f; // A | B
>f : A | B
}
else {
f; // C | D
>f : C | D
}
f; // FnTypes
>f : FnTypes
}
function checkB(f: FnTypes) {
>checkB : (f: FnTypes) => void
>f : FnTypes
if (isFunction<B>(f)) {
>isFunction<B>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : FnTypes
f; // A | B
>f : A | B
}
else {
f; // C | D
>f : C | D
}
f; // FnTypes
>f : FnTypes
}
function checkC(f: FnTypes) {
>checkC : (f: FnTypes) => void
>f : FnTypes
if (isFunction<C>(f)) {
>isFunction<C>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : FnTypes
f; // FnTypes
>f : FnTypes
}
else {
f; // never
>f : never
}
f; // FnTypes
>f : FnTypes
}
function checkD(f: FnTypes) {
>checkD : (f: FnTypes) => void
>f : FnTypes
if (isFunction<C>(f)) {
>isFunction<C>(f) : boolean
>isFunction : <T>(x: unknown) => x is T
>f : FnTypes
f; // FnTypes
>f : FnTypes
}
else {
f; // never
>f : never
}
f; // FnTypes
>f : FnTypes
}
// Type of x = y is y with freshness preserved
function fx10(obj1: { x?: number }, obj2: { x?: number, y?: number }) {
>fx10 : (obj1: { x?: number;}, obj2: { x?: number; y?: number;}) => void
>obj1 : { x?: number | undefined; }
>x : number | undefined
>obj2 : { x?: number | undefined; y?: number | undefined; }
>x : number | undefined
>y : number | undefined
obj1 = obj2 = { x: 1, y: 2 };
>obj1 = obj2 = { x: 1, y: 2 } : { x: number; y: number; }
>obj1 : { x?: number | undefined; }
>obj2 = { x: 1, y: 2 } : { x: number; y: number; }
>obj2 : { x?: number | undefined; y?: number | undefined; }
>{ x: 1, y: 2 } : { x: number; y: number; }
>x : number
>1 : 1
>y : number
>2 : 2
obj2 = obj1 = { x: 1, y: 2 };
>obj2 = obj1 = { x: 1, y: 2 } : { x: number; y: number; }
>obj2 : { x?: number | undefined; y?: number | undefined; }
>obj1 = { x: 1, y: 2 } : { x: number; y: number; }
>obj1 : { x?: number | undefined; }
>{ x: 1, y: 2 } : { x: number; y: number; }
>x : number
>1 : 1
>y : number
>2 : 2
}
function fx11(): { x?: number } {
>fx11 : () => { x?: number;}
>x : number | undefined
let obj: { x?: number, y?: number };
>obj : { x?: number | undefined; y?: number | undefined; }
>x : number | undefined
>y : number | undefined
return obj = { x: 1, y: 2 };
>obj = { x: 1, y: 2 } : { x: number; y: number; }
>obj : { x?: number | undefined; y?: number | undefined; }
>{ x: 1, y: 2 } : { x: number; y: number; }
>x : number
>1 : 1
>y : number
>2 : 2
}
@@ -51,9 +51,9 @@ function getResults1(value: Results | { data: Results }): Results {
return isResponseInData(value) ? value.data : value;
>isResponseInData : Symbol(isResponseInData, Decl(typePredicateStructuralMatch.ts, 9, 24))
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
>value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 63))
>value.data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39))
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 11, 63))
>data : Symbol(data, Decl(typePredicateStructuralMatch.ts, 15, 39))
>value : Symbol(value, Decl(typePredicateStructuralMatch.ts, 15, 21))
}
@@ -55,7 +55,7 @@ export function flatten<T extends number|TypedArray>(arr: T) {
arr[1];
>arr[1] : number
>arr : Int32Array | Uint8Array
>arr : TypedArray
>1 : 1
}
}
@@ -0,0 +1,79 @@
// @strict: true
// Check that `any` is a strict supertype of `unknown`
declare const ru1: { [x: string]: unknown };
declare const ra1: { [x: string]: any };
const a1a = [ru1, ra1]; // { [x: string]: any }[]
const a1b = [ra1, ru1]; // { [x: string]: any }[]
declare const ra2: { [x: string]: any };
declare const ru2: { [x: string]: unknown };
const a2a = [ru2, ra2]; // { [x: string]: any }[]
const a2b = [ra2, ru2]; // { [x: string]: any }[]
// Check that `{}` is strict supertype of any non-empty object
const c3 = {};
declare const r3: { [x: string]: unknown }
const a3a = [c3, r3]; // {}[]
const a3b = [r3, c3]; // {}[]
declare const r4: { [x: string]: unknown }
const c4 = {};
const a4a = [c4, r4]; // {}[]
const a4b = [r4, c4]; // {}[]
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
declare function isObject1(value: unknown): value is Record<string, unknown>;
function gg(x: {}) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
else {
x; // {}
}
x; // {}
}
declare function isObject2(value: unknown): value is {};
function gg2(x: Record<string, unknown>) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
x; // Record<string, unknown>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
type Self<T> = T extends unknown ? Identity<T> : never;
function is<T>(value: T): value is Self<T> {
return true;
}
type Union = {a: number} | {b: number} | {c: number};
function example(x: Union) {
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
x; // Union
}
@@ -0,0 +1,132 @@
// @strict: true
// Check that `any` is a strict supertype of `unknown`
declare const x11: { x: unknown };
declare const x12: { x: any };
const a11 = [x11, x12];
const a12 = [x12, x11];
declare const x21: { x: any };
declare const x22: { x: unknown };
const a21 = [x22, x21];
const a22 = [x21, x22];
// Strict subtype doesn't infer index signatures in non-fresh object types
const x31 = { a: 1 };
declare const x32: { [x: string]: unknown, a: number }
const a31 = [x31, x32];
const a32 = [x32, x31];
declare const x41: { [x: string]: unknown, a: number }
const x42 = { a: 1 };
const a41 = [x42, x41];
const a42 = [x41, x42];
// (...args: A) => R, where A is any, any[], never, or never[] and R is any or unknown, is supertype of all function types.
declare function isFunction<T>(x: unknown): x is T;
type A = (...args: any) => unknown;
type B = (...args: any[]) => unknown;
type C = (...args: never) => unknown;
type D = (...args: never[]) => unknown;
type FnTypes = A | B | C | D;
function fx1(f: (() => void) | undefined) {
if (isFunction<A>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx2(f: (() => void) | undefined) {
if (isFunction<B>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx3(f: (() => void) | undefined) {
if (isFunction<C>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function fx4(f: (() => void) | undefined) {
if (isFunction<D>(f)) {
f; // () => void
}
else {
f; // undefined
}
f; // (() => void) | undefined
}
function checkA(f: FnTypes) {
if (isFunction<A>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkB(f: FnTypes) {
if (isFunction<B>(f)) {
f; // A | B
}
else {
f; // C | D
}
f; // FnTypes
}
function checkC(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
function checkD(f: FnTypes) {
if (isFunction<C>(f)) {
f; // FnTypes
}
else {
f; // never
}
f; // FnTypes
}
// Type of x = y is y with freshness preserved
function fx10(obj1: { x?: number }, obj2: { x?: number, y?: number }) {
obj1 = obj2 = { x: 1, y: 2 };
obj2 = obj1 = { x: 1, y: 2 };
}
function fx11(): { x?: number } {
let obj: { x?: number, y?: number };
return obj = { x: 1, y: 2 };
}