Increase selectivity of subtype relationship for signatures (#35659)

* Increase selectivity of subtype relationship for signatures

* Add regression test

* Accept new baselines

* Use strictSubtypeRelation for union subtype reduction

* (x: number | undefined) -> void is subtype of (x?: number | undefined) => void

* Accept new baselines

* Add tests

* Accept new baselines

* Address CR feedback

* Fix parameter list length check

* Accept API baseline changes
This commit is contained in:
Anders Hejlsberg 2019-12-20 14:52:22 -08:00 committed by GitHub
parent 3d2b92ce69
commit 2f0d07c29a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 875 additions and 47 deletions

View File

@ -183,10 +183,12 @@ namespace ts {
NoTupleBoundsCheck = 1 << 3,
}
const enum CallbackCheck {
None,
Bivariant,
Strict,
const enum SignatureCheckMode {
BivariantCallback = 1 << 0,
StrictCallback = 1 << 1,
IgnoreReturnTypes = 1 << 2,
StrictArity = 1 << 3,
Callback = BivariantCallback | StrictCallback,
}
const enum MappedTypeModifiers {
@ -343,6 +345,7 @@ namespace ts {
assignable: assignableRelation.size,
identity: identityRelation.size,
subtype: subtypeRelation.size,
strictSubtype: strictSubtypeRelation.size,
}),
isUndefinedSymbol: symbol => symbol === undefinedSymbol,
isArgumentsSymbol: symbol => symbol === argumentsSymbol,
@ -869,6 +872,7 @@ namespace ts {
let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
const subtypeRelation = createMap<RelationComparisonResult>();
const strictSubtypeRelation = createMap<RelationComparisonResult>();
const assignableRelation = createMap<RelationComparisonResult>();
const comparableRelation = createMap<RelationComparisonResult>();
const identityRelation = createMap<RelationComparisonResult>();
@ -11428,7 +11432,7 @@ namespace ts {
}
}
count++;
if (isTypeSubtypeOf(source, target) && (
if (isTypeRelatedTo(source, target, strictSubtypeRelation) && (
!(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
!(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
isTypeDerivedFrom(source, target))) {
@ -14016,7 +14020,7 @@ namespace ts {
function isSignatureAssignableTo(source: Signature,
target: Signature,
ignoreReturnTypes: boolean): boolean {
return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes, /*reportErrors*/ false,
return compareSignaturesRelated(source, target, ignoreReturnTypes ? SignatureCheckMode.IgnoreReturnTypes : 0, /*reportErrors*/ false,
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
}
@ -14036,8 +14040,7 @@ namespace ts {
*/
function compareSignaturesRelated(source: Signature,
target: Signature,
callbackCheck: CallbackCheck,
ignoreReturnTypes: boolean,
checkMode: SignatureCheckMode,
reportErrors: boolean,
errorReporter: ErrorReporter | undefined,
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
@ -14053,7 +14056,9 @@ namespace ts {
}
const targetCount = getParameterCount(target);
if (!hasEffectiveRestParameter(target) && getMinArgumentCount(source) > targetCount) {
const sourceHasMoreParameters = !hasEffectiveRestParameter(target) &&
(checkMode & SignatureCheckMode.StrictArity ? hasEffectiveRestParameter(source) || getParameterCount(source) > targetCount : getMinArgumentCount(source) > targetCount);
if (sourceHasMoreParameters) {
return Ternary.False;
}
@ -14074,7 +14079,7 @@ namespace ts {
}
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
const strictVariance = !callbackCheck && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
let result = Ternary.True;
@ -14109,14 +14114,17 @@ namespace ts {
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
// with respect to T.
const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType));
const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType));
const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) &&
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
const related = callbacks ?
// TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
!callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
let related = callbacks ?
compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
!(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
// With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void
if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) {
related = Ternary.False;
}
if (!related) {
if (reportErrors) {
errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
@ -14128,7 +14136,7 @@ namespace ts {
result &= related;
}
if (!ignoreReturnTypes) {
if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes)) {
// If a signature resolution is already in-flight, skip issuing a circularity error
// here and just use the `any` type directly
const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType
@ -14159,7 +14167,7 @@ namespace ts {
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
// wouldn't be co-variant for T without this rule.
result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
result &= checkMode & SignatureCheckMode.BivariantCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
compareTypes(sourceReturnType, targetReturnType, reportErrors);
if (!result && reportErrors && incompatibleErrorReporter) {
incompatibleErrorReporter(sourceReturnType, targetReturnType);
@ -15467,7 +15475,7 @@ namespace ts {
}
else {
// An empty object type is related to any mapped type that includes a '?' modifier.
if (relation !== subtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
if (relation !== subtypeRelation && relation !== strictSubtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
return Ternary.True;
}
if (isGenericMappedType(target)) {
@ -15509,7 +15517,7 @@ namespace ts {
}
// Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})`
// and not `{} <- fresh({}) <- {[idx: string]: any}`
else if (relation === subtypeRelation && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
return Ternary.False;
}
// Even if relationship doesn't hold for unions, intersections, or generic type references,
@ -15854,7 +15862,7 @@ namespace ts {
if (relation === identityRelation) {
return propertiesIdenticalTo(source, target, excludedProperties);
}
const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
const requireOptionalProperties = (relation === subtypeRelation || relation === strictSubtypeRelation) && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false);
if (unmatchedProperty) {
if (reportErrors) {
@ -16077,7 +16085,7 @@ namespace ts {
*/
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
CallbackCheck.None, /*ignoreReturnTypes*/ false, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
}
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@ -16391,12 +16399,12 @@ namespace ts {
source = target;
target = temp;
}
const intersection = isIntersectionConstituent ? "&" : "";
const delimiter = isIntersectionConstituent ? ";" : ",";
if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
const typeParameters: Type[] = [];
return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters) + intersection;
return getTypeReferenceId(<TypeReference>source, typeParameters) + delimiter + getTypeReferenceId(<TypeReference>target, typeParameters);
}
return source.id + "," + target.id + intersection;
return source.id + delimiter + target.id;
}
// Invoke the callback for each underlying property symbol of the given symbol and return the first

View File

@ -3183,7 +3183,7 @@ namespace ts {
getIdentifierCount(): number;
getSymbolCount(): number;
getTypeCount(): number;
getRelationCacheSizes(): { assignable: number, identity: number, subtype: number };
getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number };
/* @internal */ getFileProcessingDiagnostics(): DiagnosticCollection;
/* @internal */ getResolvedTypeReferenceDirectives(): Map<ResolvedTypeReferenceDirective | undefined>;
@ -3501,7 +3501,7 @@ namespace ts {
/* @internal */ getIdentifierCount(): number;
/* @internal */ getSymbolCount(): number;
/* @internal */ getTypeCount(): number;
/* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number };
/* @internal */ getRelationCacheSizes(): { assignable: number, identity: number, subtype: number, strictSubtype: number };
/* @internal */ isArrayType(type: Type): boolean;
/* @internal */ isTupleType(type: Type): boolean;

View File

@ -663,6 +663,7 @@ namespace ts {
reportCountStatistic("Assignability cache size", caches.assignable);
reportCountStatistic("Identity cache size", caches.identity);
reportCountStatistic("Subtype cache size", caches.subtype);
reportCountStatistic("Strict subtype cache size", caches.strictSubtype);
performance.forEachMeasure((name, duration) => reportTimeStatistic(`${name} time`, duration));
}
else {

View File

@ -1935,6 +1935,7 @@ declare namespace ts {
assignable: number;
identity: number;
subtype: number;
strictSubtype: number;
};
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
isSourceFileDefaultLibrary(file: SourceFile): boolean;

View File

@ -1935,6 +1935,7 @@ declare namespace ts {
assignable: number;
identity: number;
subtype: number;
strictSubtype: number;
};
isSourceFileFromExternalLibrary(file: SourceFile): boolean;
isSourceFileDefaultLibrary(file: SourceFile): boolean;

View File

@ -64,8 +64,8 @@ var r5 = true ? b : a; // typeof b
>a : { x: number; y?: number; }
var r6 = true ? (x: number) => { } : (x: Object) => { }; // returns number => void
>r6 : (x: number) => void
>true ? (x: number) => { } : (x: Object) => { } : (x: number) => void
>r6 : ((x: number) => void) | ((x: Object) => void)
>true ? (x: number) => { } : (x: Object) => { } : ((x: number) => void) | ((x: Object) => void)
>true : true
>(x: number) => { } : (x: number) => void
>x : number
@ -75,7 +75,7 @@ var r6 = true ? (x: number) => { } : (x: Object) => { }; // returns number => vo
var r7: (x: Object) => void = true ? (x: number) => { } : (x: Object) => { };
>r7 : (x: Object) => void
>x : Object
>true ? (x: number) => { } : (x: Object) => { } : (x: number) => void
>true ? (x: number) => { } : (x: Object) => { } : ((x: number) => void) | ((x: Object) => void)
>true : true
>(x: number) => { } : (x: number) => void
>x : number
@ -83,8 +83,8 @@ var r7: (x: Object) => void = true ? (x: number) => { } : (x: Object) => { };
>x : Object
var r8 = true ? (x: Object) => { } : (x: number) => { }; // returns Object => void
>r8 : (x: Object) => void
>true ? (x: Object) => { } : (x: number) => { } : (x: Object) => void
>r8 : ((x: Object) => void) | ((x: number) => void)
>true ? (x: Object) => { } : (x: number) => { } : ((x: Object) => void) | ((x: number) => void)
>true : true
>(x: Object) => { } : (x: Object) => void
>x : Object

View File

@ -58,7 +58,7 @@ function f4() {
}
function f5() {
>f5 : () => Object
>f5 : () => Object | 1
return 1;
>1 : 1
@ -136,7 +136,7 @@ function f10() {
// returns number => void
function f11() {
>f11 : () => (x: number) => void
>f11 : () => ((x: number) => void) | ((x: Object) => void)
if (true) {
>true : true
@ -154,7 +154,7 @@ function f11() {
// returns Object => void
function f12() {
>f12 : () => (x: Object) => void
>f12 : () => ((x: Object) => void) | ((x: number) => void)
if (true) {
>true : true

View File

@ -566,16 +566,16 @@ function f20<T extends Number>(x: T) {
>x : T
var r19 = true ? new Object() : x; // ok
>r19 : Object
>true ? new Object() : x : Object
>r19 : Object | T
>true ? new Object() : x : Object | T
>true : true
>new Object() : Object
>Object : ObjectConstructor
>x : T
var r19 = true ? x : new Object(); // ok
>r19 : Object
>true ? x : new Object() : Object
>r19 : Object | T
>true ? x : new Object() : Object | T
>true : true
>x : T
>new Object() : Object

View File

@ -3,8 +3,8 @@ var x: string|number;
>x : string | number
var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed;
>r1 : (from: number, length?: number) => string
>typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed : (from: number, length?: number) => string
>r1 : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>typeof x === "string" && typeof x === "string" : boolean
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
@ -22,8 +22,8 @@ var r1 = typeof x === "string" && typeof x === "string" ? x.substr : x.toFixed;
>toFixed : (fractionDigits?: number) => string
var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr;
>r2 : (from: number, length?: number) => string
>!(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr : (from: number, length?: number) => string
>r2 : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>!(typeof x === "string" && typeof x === "string") ? x.toFixed : x.substr : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>!(typeof x === "string" && typeof x === "string") : boolean
>(typeof x === "string" && typeof x === "string") : boolean
>typeof x === "string" && typeof x === "string" : boolean
@ -43,8 +43,8 @@ var r2 = !(typeof x === "string" && typeof x === "string") ? x.toFixed : x.subst
>substr : (from: number, length?: number) => string
var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed;
>r3 : (from: number, length?: number) => string
>typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed : (from: number, length?: number) => string
>r3 : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>typeof x === "string" || typeof x === "string" : boolean
>typeof x === "string" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
@ -62,8 +62,8 @@ var r3 = typeof x === "string" || typeof x === "string" ? x.substr : x.toFixed;
>toFixed : (fractionDigits?: number) => string
var r4 = !(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr;
>r4 : (from: number, length?: number) => string
>!(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr : (from: number, length?: number) => string
>r4 : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>!(typeof x === "string" || typeof x === "string") ? x.toFixed : x.substr : ((from: number, length?: number) => string) | ((fractionDigits?: number) => string)
>!(typeof x === "string" || typeof x === "string") : boolean
>(typeof x === "string" || typeof x === "string") : boolean
>typeof x === "string" || typeof x === "string" : boolean

View File

@ -0,0 +1,25 @@
//// [unionReductionMutualSubtypes.ts]
// Repro from #35414
interface ReturnVal {
something(): void;
}
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}
//// [unionReductionMutualSubtypes.js]
"use strict";
// Repro from #35414
var k = { something: function () { } };
function run(options) {
var _a;
var something = (_a = options.something) !== null && _a !== void 0 ? _a : val.something;
something('');
}

View File

@ -0,0 +1,38 @@
=== tests/cases/compiler/unionReductionMutualSubtypes.ts ===
// Repro from #35414
interface ReturnVal {
>ReturnVal : Symbol(ReturnVal, Decl(unionReductionMutualSubtypes.ts, 0, 0))
something(): void;
>something : Symbol(ReturnVal.something, Decl(unionReductionMutualSubtypes.ts, 2, 21))
}
const k: ReturnVal = { something() { } }
>k : Symbol(k, Decl(unionReductionMutualSubtypes.ts, 6, 5))
>ReturnVal : Symbol(ReturnVal, Decl(unionReductionMutualSubtypes.ts, 0, 0))
>something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 6, 22))
declare const val: ReturnVal;
>val : Symbol(val, Decl(unionReductionMutualSubtypes.ts, 8, 13))
>ReturnVal : Symbol(ReturnVal, Decl(unionReductionMutualSubtypes.ts, 0, 0))
function run(options: { something?(b?: string): void }) {
>run : Symbol(run, Decl(unionReductionMutualSubtypes.ts, 8, 29))
>options : Symbol(options, Decl(unionReductionMutualSubtypes.ts, 9, 13))
>something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 9, 23))
>b : Symbol(b, Decl(unionReductionMutualSubtypes.ts, 9, 35))
const something = options.something ?? val.something;
>something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 10, 9))
>options.something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 9, 23))
>options : Symbol(options, Decl(unionReductionMutualSubtypes.ts, 9, 13))
>something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 9, 23))
>val.something : Symbol(ReturnVal.something, Decl(unionReductionMutualSubtypes.ts, 2, 21))
>val : Symbol(val, Decl(unionReductionMutualSubtypes.ts, 8, 13))
>something : Symbol(ReturnVal.something, Decl(unionReductionMutualSubtypes.ts, 2, 21))
something('');
>something : Symbol(something, Decl(unionReductionMutualSubtypes.ts, 10, 9))
}

View File

@ -0,0 +1,38 @@
=== tests/cases/compiler/unionReductionMutualSubtypes.ts ===
// Repro from #35414
interface ReturnVal {
something(): void;
>something : () => void
}
const k: ReturnVal = { something() { } }
>k : ReturnVal
>{ something() { } } : { something(): void; }
>something : () => void
declare const val: ReturnVal;
>val : ReturnVal
function run(options: { something?(b?: string): void }) {
>run : (options: { something?(b?: string | undefined): void; }) => void
>options : { something?(b?: string | undefined): void; }
>something : ((b?: string | undefined) => void) | undefined
>b : string | undefined
const something = options.something ?? val.something;
>something : (b?: string | undefined) => void
>options.something ?? val.something : (b?: string | undefined) => void
>options.something : ((b?: string | undefined) => void) | undefined
>options : { something?(b?: string | undefined): void; }
>something : ((b?: string | undefined) => void) | undefined
>val.something : () => void
>val : ReturnVal
>something : () => void
something('');
>something('') : void
>something : (b?: string | undefined) => void
>'' : ""
}

View File

@ -0,0 +1,72 @@
tests/cases/conformance/types/union/unionTypeReduction2.ts(33,5): error TS2554: Expected 1 arguments, but got 0.
==== tests/cases/conformance/types/union/unionTypeReduction2.ts (1 errors) ====
function f1(x: { f(): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f2(x: { f(x: string | undefined): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f3(x: () => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f4(x: (x: string | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f5(x: (x: string | undefined) => void, y: (x?: 'hello') => void) {
let f = !!true ? x : y; // (x?: 'hello') => void
f();
f('hello');
}
function f6(x: (x: 'hello' | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x: 'hello' | undefined) => void
f(); // Error
~~~
!!! error TS2554: Expected 1 arguments, but got 0.
!!! related TS6210 tests/cases/conformance/types/union/unionTypeReduction2.ts:31:17: An argument for 'x' was not provided.
f('hello');
}
type A = {
f(): void;
}
type B = {
f(x?: string): void;
g(): void;
}
function f11(a: A, b: B) {
let z = !!true ? a : b; // A | B
z.f();
z.f('hello');
}
// Repro from #35414
interface ReturnVal {
something(): void;
}
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}

View File

@ -0,0 +1,110 @@
//// [unionTypeReduction2.ts]
function f1(x: { f(): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f2(x: { f(x: string | undefined): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f3(x: () => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f4(x: (x: string | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f5(x: (x: string | undefined) => void, y: (x?: 'hello') => void) {
let f = !!true ? x : y; // (x?: 'hello') => void
f();
f('hello');
}
function f6(x: (x: 'hello' | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x: 'hello' | undefined) => void
f(); // Error
f('hello');
}
type A = {
f(): void;
}
type B = {
f(x?: string): void;
g(): void;
}
function f11(a: A, b: B) {
let z = !!true ? a : b; // A | B
z.f();
z.f('hello');
}
// Repro from #35414
interface ReturnVal {
something(): void;
}
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}
//// [unionTypeReduction2.js]
"use strict";
function f1(x, y) {
var z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f2(x, y) {
var z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f3(x, y) {
var f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f4(x, y) {
var f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f5(x, y) {
var f = !!true ? x : y; // (x?: 'hello') => void
f();
f('hello');
}
function f6(x, y) {
var f = !!true ? x : y; // (x: 'hello' | undefined) => void
f(); // Error
f('hello');
}
function f11(a, b) {
var z = !!true ? a : b; // A | B
z.f();
z.f('hello');
}
var k = { something: function () { } };
function run(options) {
var _a;
var something = (_a = options.something) !== null && _a !== void 0 ? _a : val.something;
something('');
}

View File

@ -0,0 +1,203 @@
=== tests/cases/conformance/types/union/unionTypeReduction2.ts ===
function f1(x: { f(): void }, y: { f(x?: string): void }) {
>f1 : Symbol(f1, Decl(unionTypeReduction2.ts, 0, 0))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 0, 12))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 16))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 0, 29))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 34))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 0, 37))
let z = !!true ? x : y; // { f(x?: string): void }
>z : Symbol(z, Decl(unionTypeReduction2.ts, 1, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 0, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 0, 29))
z.f();
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 34))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 1, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 34))
z.f('hello');
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 34))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 1, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 0, 34))
}
function f2(x: { f(x: string | undefined): void }, y: { f(x?: string): void }) {
>f2 : Symbol(f2, Decl(unionTypeReduction2.ts, 4, 1))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 6, 12))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 16))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 6, 19))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 6, 50))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 55))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 6, 58))
let z = !!true ? x : y; // { f(x?: string): void }
>z : Symbol(z, Decl(unionTypeReduction2.ts, 7, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 6, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 6, 50))
z.f();
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 55))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 7, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 55))
z.f('hello');
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 55))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 7, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 6, 55))
}
function f3(x: () => void, y: (x?: string) => void) {
>f3 : Symbol(f3, Decl(unionTypeReduction2.ts, 10, 1))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 12, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 12, 26))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 12, 31))
let f = !!true ? x : y; // (x?: string) => void
>f : Symbol(f, Decl(unionTypeReduction2.ts, 13, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 12, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 12, 26))
f();
>f : Symbol(f, Decl(unionTypeReduction2.ts, 13, 7))
f('hello');
>f : Symbol(f, Decl(unionTypeReduction2.ts, 13, 7))
}
function f4(x: (x: string | undefined) => void, y: (x?: string) => void) {
>f4 : Symbol(f4, Decl(unionTypeReduction2.ts, 16, 1))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 18, 12))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 18, 16))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 18, 47))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 18, 52))
let f = !!true ? x : y; // (x?: string) => void
>f : Symbol(f, Decl(unionTypeReduction2.ts, 19, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 18, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 18, 47))
f();
>f : Symbol(f, Decl(unionTypeReduction2.ts, 19, 7))
f('hello');
>f : Symbol(f, Decl(unionTypeReduction2.ts, 19, 7))
}
function f5(x: (x: string | undefined) => void, y: (x?: 'hello') => void) {
>f5 : Symbol(f5, Decl(unionTypeReduction2.ts, 22, 1))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 24, 12))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 24, 16))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 24, 47))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 24, 52))
let f = !!true ? x : y; // (x?: 'hello') => void
>f : Symbol(f, Decl(unionTypeReduction2.ts, 25, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 24, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 24, 47))
f();
>f : Symbol(f, Decl(unionTypeReduction2.ts, 25, 7))
f('hello');
>f : Symbol(f, Decl(unionTypeReduction2.ts, 25, 7))
}
function f6(x: (x: 'hello' | undefined) => void, y: (x?: string) => void) {
>f6 : Symbol(f6, Decl(unionTypeReduction2.ts, 28, 1))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 30, 12))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 30, 16))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 30, 48))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 30, 53))
let f = !!true ? x : y; // (x: 'hello' | undefined) => void
>f : Symbol(f, Decl(unionTypeReduction2.ts, 31, 7))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 30, 12))
>y : Symbol(y, Decl(unionTypeReduction2.ts, 30, 48))
f(); // Error
>f : Symbol(f, Decl(unionTypeReduction2.ts, 31, 7))
f('hello');
>f : Symbol(f, Decl(unionTypeReduction2.ts, 31, 7))
}
type A = {
>A : Symbol(A, Decl(unionTypeReduction2.ts, 34, 1))
f(): void;
>f : Symbol(f, Decl(unionTypeReduction2.ts, 36, 10))
}
type B = {
>B : Symbol(B, Decl(unionTypeReduction2.ts, 38, 1))
f(x?: string): void;
>f : Symbol(f, Decl(unionTypeReduction2.ts, 40, 10))
>x : Symbol(x, Decl(unionTypeReduction2.ts, 41, 6))
g(): void;
>g : Symbol(g, Decl(unionTypeReduction2.ts, 41, 24))
}
function f11(a: A, b: B) {
>f11 : Symbol(f11, Decl(unionTypeReduction2.ts, 43, 1))
>a : Symbol(a, Decl(unionTypeReduction2.ts, 45, 13))
>A : Symbol(A, Decl(unionTypeReduction2.ts, 34, 1))
>b : Symbol(b, Decl(unionTypeReduction2.ts, 45, 18))
>B : Symbol(B, Decl(unionTypeReduction2.ts, 38, 1))
let z = !!true ? a : b; // A | B
>z : Symbol(z, Decl(unionTypeReduction2.ts, 46, 7))
>a : Symbol(a, Decl(unionTypeReduction2.ts, 45, 13))
>b : Symbol(b, Decl(unionTypeReduction2.ts, 45, 18))
z.f();
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 36, 10), Decl(unionTypeReduction2.ts, 40, 10))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 46, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 36, 10), Decl(unionTypeReduction2.ts, 40, 10))
z.f('hello');
>z.f : Symbol(f, Decl(unionTypeReduction2.ts, 36, 10), Decl(unionTypeReduction2.ts, 40, 10))
>z : Symbol(z, Decl(unionTypeReduction2.ts, 46, 7))
>f : Symbol(f, Decl(unionTypeReduction2.ts, 36, 10), Decl(unionTypeReduction2.ts, 40, 10))
}
// Repro from #35414
interface ReturnVal {
>ReturnVal : Symbol(ReturnVal, Decl(unionTypeReduction2.ts, 49, 1))
something(): void;
>something : Symbol(ReturnVal.something, Decl(unionTypeReduction2.ts, 53, 21))
}
const k: ReturnVal = { something() { } }
>k : Symbol(k, Decl(unionTypeReduction2.ts, 57, 5))
>ReturnVal : Symbol(ReturnVal, Decl(unionTypeReduction2.ts, 49, 1))
>something : Symbol(something, Decl(unionTypeReduction2.ts, 57, 22))
declare const val: ReturnVal;
>val : Symbol(val, Decl(unionTypeReduction2.ts, 59, 13))
>ReturnVal : Symbol(ReturnVal, Decl(unionTypeReduction2.ts, 49, 1))
function run(options: { something?(b?: string): void }) {
>run : Symbol(run, Decl(unionTypeReduction2.ts, 59, 29))
>options : Symbol(options, Decl(unionTypeReduction2.ts, 60, 13))
>something : Symbol(something, Decl(unionTypeReduction2.ts, 60, 23))
>b : Symbol(b, Decl(unionTypeReduction2.ts, 60, 35))
const something = options.something ?? val.something;
>something : Symbol(something, Decl(unionTypeReduction2.ts, 61, 9))
>options.something : Symbol(something, Decl(unionTypeReduction2.ts, 60, 23))
>options : Symbol(options, Decl(unionTypeReduction2.ts, 60, 13))
>something : Symbol(something, Decl(unionTypeReduction2.ts, 60, 23))
>val.something : Symbol(ReturnVal.something, Decl(unionTypeReduction2.ts, 53, 21))
>val : Symbol(val, Decl(unionTypeReduction2.ts, 59, 13))
>something : Symbol(ReturnVal.something, Decl(unionTypeReduction2.ts, 53, 21))
something('');
>something : Symbol(something, Decl(unionTypeReduction2.ts, 61, 9))
}

View File

@ -0,0 +1,250 @@
=== tests/cases/conformance/types/union/unionTypeReduction2.ts ===
function f1(x: { f(): void }, y: { f(x?: string): void }) {
>f1 : (x: { f(): void; }, y: { f(x?: string | undefined): void; }) => void
>x : { f(): void; }
>f : () => void
>y : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
>x : string | undefined
let z = !!true ? x : y; // { f(x?: string): void }
>z : { f(x?: string | undefined): void; }
>!!true ? x : y : { f(x?: string | undefined): void; }
>!!true : true
>!true : false
>true : true
>x : { f(): void; }
>y : { f(x?: string | undefined): void; }
z.f();
>z.f() : void
>z.f : (x?: string | undefined) => void
>z : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
z.f('hello');
>z.f('hello') : void
>z.f : (x?: string | undefined) => void
>z : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
>'hello' : "hello"
}
function f2(x: { f(x: string | undefined): void }, y: { f(x?: string): void }) {
>f2 : (x: { f(x: string | undefined): void; }, y: { f(x?: string | undefined): void; }) => void
>x : { f(x: string | undefined): void; }
>f : (x: string | undefined) => void
>x : string | undefined
>y : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
>x : string | undefined
let z = !!true ? x : y; // { f(x?: string): void }
>z : { f(x?: string | undefined): void; }
>!!true ? x : y : { f(x?: string | undefined): void; }
>!!true : true
>!true : false
>true : true
>x : { f(x: string | undefined): void; }
>y : { f(x?: string | undefined): void; }
z.f();
>z.f() : void
>z.f : (x?: string | undefined) => void
>z : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
z.f('hello');
>z.f('hello') : void
>z.f : (x?: string | undefined) => void
>z : { f(x?: string | undefined): void; }
>f : (x?: string | undefined) => void
>'hello' : "hello"
}
function f3(x: () => void, y: (x?: string) => void) {
>f3 : (x: () => void, y: (x?: string | undefined) => void) => void
>x : () => void
>y : (x?: string | undefined) => void
>x : string | undefined
let f = !!true ? x : y; // (x?: string) => void
>f : (x?: string | undefined) => void
>!!true ? x : y : (x?: string | undefined) => void
>!!true : true
>!true : false
>true : true
>x : () => void
>y : (x?: string | undefined) => void
f();
>f() : void
>f : (x?: string | undefined) => void
f('hello');
>f('hello') : void
>f : (x?: string | undefined) => void
>'hello' : "hello"
}
function f4(x: (x: string | undefined) => void, y: (x?: string) => void) {
>f4 : (x: (x: string | undefined) => void, y: (x?: string | undefined) => void) => void
>x : (x: string | undefined) => void
>x : string | undefined
>y : (x?: string | undefined) => void
>x : string | undefined
let f = !!true ? x : y; // (x?: string) => void
>f : (x?: string | undefined) => void
>!!true ? x : y : (x?: string | undefined) => void
>!!true : true
>!true : false
>true : true
>x : (x: string | undefined) => void
>y : (x?: string | undefined) => void
f();
>f() : void
>f : (x?: string | undefined) => void
f('hello');
>f('hello') : void
>f : (x?: string | undefined) => void
>'hello' : "hello"
}
function f5(x: (x: string | undefined) => void, y: (x?: 'hello') => void) {
>f5 : (x: (x: string | undefined) => void, y: (x?: "hello" | undefined) => void) => void
>x : (x: string | undefined) => void
>x : string | undefined
>y : (x?: "hello" | undefined) => void
>x : "hello" | undefined
let f = !!true ? x : y; // (x?: 'hello') => void
>f : (x?: "hello" | undefined) => void
>!!true ? x : y : (x?: "hello" | undefined) => void
>!!true : true
>!true : false
>true : true
>x : (x: string | undefined) => void
>y : (x?: "hello" | undefined) => void
f();
>f() : void
>f : (x?: "hello" | undefined) => void
f('hello');
>f('hello') : void
>f : (x?: "hello" | undefined) => void
>'hello' : "hello"
}
function f6(x: (x: 'hello' | undefined) => void, y: (x?: string) => void) {
>f6 : (x: (x: "hello" | undefined) => void, y: (x?: string | undefined) => void) => void
>x : (x: "hello" | undefined) => void
>x : "hello" | undefined
>y : (x?: string | undefined) => void
>x : string | undefined
let f = !!true ? x : y; // (x: 'hello' | undefined) => void
>f : (x: "hello" | undefined) => void
>!!true ? x : y : (x: "hello" | undefined) => void
>!!true : true
>!true : false
>true : true
>x : (x: "hello" | undefined) => void
>y : (x?: string | undefined) => void
f(); // Error
>f() : void
>f : (x: "hello" | undefined) => void
f('hello');
>f('hello') : void
>f : (x: "hello" | undefined) => void
>'hello' : "hello"
}
type A = {
>A : A
f(): void;
>f : () => void
}
type B = {
>B : B
f(x?: string): void;
>f : (x?: string | undefined) => void
>x : string | undefined
g(): void;
>g : () => void
}
function f11(a: A, b: B) {
>f11 : (a: A, b: B) => void
>a : A
>b : B
let z = !!true ? a : b; // A | B
>z : A | B
>!!true ? a : b : A | B
>!!true : true
>!true : false
>true : true
>a : A
>b : B
z.f();
>z.f() : void
>z.f : ((x?: string | undefined) => void) | (() => void)
>z : A | B
>f : ((x?: string | undefined) => void) | (() => void)
z.f('hello');
>z.f('hello') : void
>z.f : ((x?: string | undefined) => void) | (() => void)
>z : A | B
>f : ((x?: string | undefined) => void) | (() => void)
>'hello' : "hello"
}
// Repro from #35414
interface ReturnVal {
something(): void;
>something : () => void
}
const k: ReturnVal = { something() { } }
>k : ReturnVal
>{ something() { } } : { something(): void; }
>something : () => void
declare const val: ReturnVal;
>val : ReturnVal
function run(options: { something?(b?: string): void }) {
>run : (options: { something?(b?: string | undefined): void; }) => void
>options : { something?(b?: string | undefined): void; }
>something : ((b?: string | undefined) => void) | undefined
>b : string | undefined
const something = options.something ?? val.something;
>something : (b?: string | undefined) => void
>options.something ?? val.something : (b?: string | undefined) => void
>options.something : ((b?: string | undefined) => void) | undefined
>options : { something?(b?: string | undefined): void; }
>something : ((b?: string | undefined) => void) | undefined
>val.something : () => void
>val : ReturnVal
>something : () => void
something('');
>something('') : void
>something : (b?: string | undefined) => void
>'' : ""
}

View File

@ -0,0 +1,15 @@
// @strict: true
// Repro from #35414
interface ReturnVal {
something(): void;
}
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}

View File

@ -0,0 +1,66 @@
// @strict: true
function f1(x: { f(): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f2(x: { f(x: string | undefined): void }, y: { f(x?: string): void }) {
let z = !!true ? x : y; // { f(x?: string): void }
z.f();
z.f('hello');
}
function f3(x: () => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f4(x: (x: string | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x?: string) => void
f();
f('hello');
}
function f5(x: (x: string | undefined) => void, y: (x?: 'hello') => void) {
let f = !!true ? x : y; // (x?: 'hello') => void
f();
f('hello');
}
function f6(x: (x: 'hello' | undefined) => void, y: (x?: string) => void) {
let f = !!true ? x : y; // (x: 'hello' | undefined) => void
f(); // Error
f('hello');
}
type A = {
f(): void;
}
type B = {
f(x?: string): void;
g(): void;
}
function f11(a: A, b: B) {
let z = !!true ? a : b; // A | B
z.f();
z.f('hello');
}
// Repro from #35414
interface ReturnVal {
something(): void;
}
const k: ReturnVal = { something() { } }
declare const val: ReturnVal;
function run(options: { something?(b?: string): void }) {
const something = options.something ?? val.something;
something('');
}