diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b1d258e6c2afd..521e6157b79c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1251,12 +1251,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 { @@ -19571,12 +19572,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; } /** @@ -19595,9 +19599,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) && @@ -19853,7 +19860,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, 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 && @@ -21475,8 +21483,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; } @@ -22131,8 +22139,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); } @@ -22228,8 +22239,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) { @@ -27069,21 +27082,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true); } - 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. @@ -27095,7 +27112,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 ? @@ -36492,7 +36509,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)) { diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index 3c2bfaa2f7cf0..5251973005fcd 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -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)) { diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 23e6034ce1cf0..9c188295a82f0 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -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; } diff --git a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types index 395da42f87632..f79e73f03e4d6 100644 --- a/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types +++ b/tests/baselines/reference/controlFlowFavorAssertedTypeThroughTypePredicate.types @@ -23,7 +23,7 @@ if (isObject1(obj1)) { } // check type after conditional block obj1; ->obj1 : Record +>obj1 : {} declare const obj2: {} | undefined; >obj2 : {} | undefined @@ -43,7 +43,7 @@ if (isObject1(obj2)) { } // check type after conditional block obj2; ->obj2 : Record | 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 declare const obj4: Record | undefined; >obj4 : Record | undefined @@ -87,5 +87,5 @@ if (isObject2(obj4)) { } // check type after conditional block obj4; ->obj4 : {} | undefined +>obj4 : Record | undefined diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols index 868d1c1fd784e..601699b93f716 100644 --- a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols @@ -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)) diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types index 6aeba6f1b733a..113c994c6e619 100644 --- a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types @@ -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)) { diff --git a/tests/baselines/reference/narrowingMutualSubtypes.js b/tests/baselines/reference/narrowingMutualSubtypes.js new file mode 100644 index 0000000000000..0d5f36d2f9551 --- /dev/null +++ b/tests/baselines/reference/narrowingMutualSubtypes.js @@ -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; + +function gg(x: {}) { + if (isObject1(x)) { + x; // Record + } + else { + x; // {} + } + x; // {} +} + +declare function isObject2(value: unknown): value is {}; + +function gg2(x: Record) { + if (isObject2(x)) { + x; // {} + } + else { + x; // Record + } + x; // Record +} + +// Repro from #50916 + +type Identity = {[K in keyof T]: T[K]}; + +type Self = T extends unknown ? Identity : never; + +function is(value: T): value is Self { + 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 + } + else { + x; // {} + } + x; // {} +} +function gg2(x) { + if (isObject2(x)) { + x; // {} + } + else { + x; // Record + } + x; // Record +} +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 +} diff --git a/tests/baselines/reference/narrowingMutualSubtypes.symbols b/tests/baselines/reference/narrowingMutualSubtypes.symbols new file mode 100644 index 0000000000000..9229c0b6e2946 --- /dev/null +++ b/tests/baselines/reference/narrowingMutualSubtypes.symbols @@ -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; +>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 +>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) { +>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 +>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13)) + } + x; // Record +>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13)) +} + +// Repro from #50916 + +type Identity = {[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 extends unknown ? Identity : 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(value: T): value is Self { +>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)) +} + diff --git a/tests/baselines/reference/narrowingMutualSubtypes.types b/tests/baselines/reference/narrowingMutualSubtypes.types new file mode 100644 index 0000000000000..7cf817697aafb --- /dev/null +++ b/tests/baselines/reference/narrowingMutualSubtypes.types @@ -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; +>isObject1 : (value: unknown) => value is Record +>value : unknown + +function gg(x: {}) { +>gg : (x: {}) => void +>x : {} + + if (isObject1(x)) { +>isObject1(x) : boolean +>isObject1 : (value: unknown) => value is Record +>x : {} + + x; // Record +>x : Record + } + else { + x; // {} +>x : {} + } + x; // {} +>x : {} +} + +declare function isObject2(value: unknown): value is {}; +>isObject2 : (value: unknown) => value is {} +>value : unknown + +function gg2(x: Record) { +>gg2 : (x: Record) => void +>x : Record + + if (isObject2(x)) { +>isObject2(x) : boolean +>isObject2 : (value: unknown) => value is {} +>x : Record + + x; // {} +>x : {} + } + else { + x; // Record +>x : Record + } + x; // Record +>x : Record +} + +// Repro from #50916 + +type Identity = {[K in keyof T]: T[K]}; +>Identity : Identity + +type Self = T extends unknown ? Identity : never; +>Self : Self + +function is(value: T): value is Self { +>is : (value: T) => value is Self +>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 : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + if (is(x)) {} +>is(x) : boolean +>is : (value: T) => value is Self +>x : Union + + x; // Union +>x : Union +} + diff --git a/tests/baselines/reference/strictSubtypeAndNarrowing.errors.txt b/tests/baselines/reference/strictSubtypeAndNarrowing.errors.txt new file mode 100644 index 0000000000000..1dd63fe6e0f77 --- /dev/null +++ b/tests/baselines/reference/strictSubtypeAndNarrowing.errors.txt @@ -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(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(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined + } + + function fx2(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined + } + + function fx3(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined + } + + function fx4(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined + } + + function checkA(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes + } + + function checkB(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes + } + + function checkC(f: FnTypes) { + if (isFunction(f)) { + f; // FnTypes + } + else { + f; // never + } + f; // FnTypes + } + + function checkD(f: FnTypes) { + if (isFunction(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; }'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/strictSubtypeAndNarrowing.js b/tests/baselines/reference/strictSubtypeAndNarrowing.js new file mode 100644 index 0000000000000..3e135c3af6fc1 --- /dev/null +++ b/tests/baselines/reference/strictSubtypeAndNarrowing.js @@ -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(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(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx2(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx3(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx4(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function checkA(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes +} + +function checkB(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes +} + +function checkC(f: FnTypes) { + if (isFunction(f)) { + f; // FnTypes + } + else { + f; // never + } + f; // FnTypes +} + +function checkD(f: FnTypes) { + if (isFunction(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 }; +} diff --git a/tests/baselines/reference/strictSubtypeAndNarrowing.symbols b/tests/baselines/reference/strictSubtypeAndNarrowing.symbols new file mode 100644 index 0000000000000..2bdf3b7777cae --- /dev/null +++ b/tests/baselines/reference/strictSubtypeAndNarrowing.symbols @@ -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(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(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(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(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(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(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(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(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(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)) +} + diff --git a/tests/baselines/reference/strictSubtypeAndNarrowing.types b/tests/baselines/reference/strictSubtypeAndNarrowing.types new file mode 100644 index 0000000000000..8f2e6a2cd8c83 --- /dev/null +++ b/tests/baselines/reference/strictSubtypeAndNarrowing.types @@ -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(x: unknown): x is T; +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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(f)) { +>isFunction(f) : boolean +>isFunction : (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 +} + diff --git a/tests/baselines/reference/typePredicateStructuralMatch.symbols b/tests/baselines/reference/typePredicateStructuralMatch.symbols index 75fea1027d2c3..5e4826ebbf2e8 100644 --- a/tests/baselines/reference/typePredicateStructuralMatch.symbols +++ b/tests/baselines/reference/typePredicateStructuralMatch.symbols @@ -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)) } diff --git a/tests/baselines/reference/unionWithIndexSignature.types b/tests/baselines/reference/unionWithIndexSignature.types index 2bc7d0fbe5c9e..7aa5877e37a25 100644 --- a/tests/baselines/reference/unionWithIndexSignature.types +++ b/tests/baselines/reference/unionWithIndexSignature.types @@ -55,7 +55,7 @@ export function flatten(arr: T) { arr[1]; >arr[1] : number ->arr : Int32Array | Uint8Array +>arr : TypedArray >1 : 1 } } diff --git a/tests/cases/compiler/narrowingMutualSubtypes.ts b/tests/cases/compiler/narrowingMutualSubtypes.ts new file mode 100644 index 0000000000000..1d666c256fa77 --- /dev/null +++ b/tests/cases/compiler/narrowingMutualSubtypes.ts @@ -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; + +function gg(x: {}) { + if (isObject1(x)) { + x; // Record + } + else { + x; // {} + } + x; // {} +} + +declare function isObject2(value: unknown): value is {}; + +function gg2(x: Record) { + if (isObject2(x)) { + x; // {} + } + else { + x; // Record + } + x; // Record +} + +// Repro from #50916 + +type Identity = {[K in keyof T]: T[K]}; + +type Self = T extends unknown ? Identity : never; + +function is(value: T): value is Self { + 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 +} diff --git a/tests/cases/compiler/strictSubtypeAndNarrowing.ts b/tests/cases/compiler/strictSubtypeAndNarrowing.ts new file mode 100644 index 0000000000000..6239072f97efe --- /dev/null +++ b/tests/cases/compiler/strictSubtypeAndNarrowing.ts @@ -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(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(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx2(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx3(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function fx4(f: (() => void) | undefined) { + if (isFunction(f)) { + f; // () => void + } + else { + f; // undefined + } + f; // (() => void) | undefined +} + +function checkA(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes +} + +function checkB(f: FnTypes) { + if (isFunction(f)) { + f; // A | B + } + else { + f; // C | D + } + f; // FnTypes +} + +function checkC(f: FnTypes) { + if (isFunction(f)) { + f; // FnTypes + } + else { + f; // never + } + f; // FnTypes +} + +function checkD(f: FnTypes) { + if (isFunction(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 }; +}