diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4b4c1813ac46d..b81ee0b8864d8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8261,28 +8261,18 @@ namespace ts { if (checkType === wildcardType || extendsType === wildcardType) { return wildcardType; } - // If this is a distributive conditional type and the check type is generic, we need to defer + // If this is a distributive conditional type and the check type is generic we need to defer // resolution of the conditional type such that a later instantiation will properly distribute // over union types. if (!root.isDistributive || !maybeTypeOfKind(checkType, TypeFlags.Instantiable)) { - // Return falseType for a definitely false extends check. We check an instantations of the two - // types with type parameters mapped to the wildcard type, the most permissive instantiations - // possible (the wildcard type is assignable to and from all types). If those are not related, - // then no instatiations will be and we can just return the false branch type. - if (!isTypeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(extendsType))) { - return instantiateType(root.falseType, mapper); - } - // The check could be true for some instantiation let combinedMapper: TypeMapper; if (root.inferTypeParameters) { - const inferences = map(root.inferTypeParameters, createInferenceInfo); + const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); // We don't want inferences from constraints as they may cause us to eagerly resolve the // conditional type instead of deferring resolution. Also, we always want strict function // types rules (i.e. proper contravariance) for inferences. - inferTypes(inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); - // We infer {} when there are no candidates for a type parameter - const inferredTypes = map(inferences, inference => getTypeFromInference(inference) || emptyObjectType); - combinedMapper = combineTypeMappers(mapper, createTypeMapper(root.inferTypeParameters, inferredTypes)); + inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); + combinedMapper = combineTypeMappers(mapper, context); } // Return union of trueType and falseType for 'any' since it matches anything if (checkType.flags & TypeFlags.Any) { @@ -8290,6 +8280,13 @@ namespace ts { } // Instantiate the extends type including inferences for 'infer T' type parameters const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; + // Return falseType for a definitely false extends check. We check an instantations of the two + // types with type parameters mapped to the wildcard type, the most permissive instantiations + // possible (the wildcard type is assignable to and from all types). If those are not related, + // then no instatiations will be and we can just return the false branch type. + if (!isTypeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(inferredExtendsType))) { + return instantiateType(root.falseType, mapper); + } // Return trueType for a definitely true extends check. The definitely assignable relation excludes // type variable constraints from consideration. Without the definitely assignable relation, the type // type Foo = T extends { x: string } ? string : number @@ -8735,12 +8732,12 @@ namespace ts { } function isInferenceContext(mapper: TypeMapper): mapper is InferenceContext { - return !!(mapper).signature; + return !!(mapper).typeParameters; } function cloneTypeMapper(mapper: TypeMapper): TypeMapper { return mapper && isInferenceContext(mapper) ? - createInferenceContext(mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) : + createInferenceContext(mapper.typeParameters, mapper.signature, mapper.flags | InferenceFlags.NoDefault, mapper.compareTypes, mapper.inferences) : mapper; } @@ -11395,9 +11392,10 @@ namespace ts { } } - function createInferenceContext(signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { - const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(signature.typeParameters, createInferenceInfo); + function createInferenceContext(typeParameters: TypeParameter[], signature: Signature, flags: InferenceFlags, compareTypes?: TypeComparer, baseInferences?: InferenceInfo[]): InferenceContext { + const inferences = baseInferences ? map(baseInferences, cloneInferenceInfo) : map(typeParameters, createInferenceInfo); const context = mapper as InferenceContext; + context.typeParameters = typeParameters; context.signature = signature; context.inferences = inferences; context.flags = flags; @@ -11526,7 +11524,7 @@ namespace ts { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || emptyObjectType; + return getTypeFromInference(inference); } function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) { @@ -11545,7 +11543,7 @@ namespace ts { function getTypeFromInference(inference: InferenceInfo) { return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : inference.contraCandidates ? getIntersectionType(inference.contraCandidates) : - undefined; + emptyObjectType; } function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) { @@ -11918,63 +11916,68 @@ namespace ts { const inference = context.inferences[index]; let inferredType = inference.inferredType; if (!inferredType) { - if (inference.candidates) { - // Extract all object literal types and replace them with a single widened and normalized type. - const candidates = widenObjectLiteralCandidates(inference.candidates); - // We widen inferred literal types if - // all inferences were made to top-level ocurrences of the type parameter, and - // the type parameter has no constraint or its constraint includes no primitive or literal types, and - // the type parameter was fixed during inference or does not occur at top-level in the return type. - const signature = context.signature; - const widenLiteralTypes = inference.topLevel && - !hasPrimitiveConstraint(inference.typeParameter) && - (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); - const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates; - // If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if - // union types were requested or if all inferences were made from the return type position, infer a - // union type. Otherwise, infer a common supertype. - const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? - getUnionType(baseCandidates, UnionReduction.Subtype) : - getCommonSupertype(baseCandidates); - inferredType = getWidenedType(unwidenedType); - // If we have inferred 'never' but have contravariant candidates. To get a more specific type we - // infer from the contravariant candidates instead. - if (inferredType.flags & TypeFlags.Never && inference.contraCandidates) { + const signature = context.signature; + if (signature) { + if (inference.candidates) { + // Extract all object literal types and replace them with a single widened and normalized type. + const candidates = widenObjectLiteralCandidates(inference.candidates); + // We widen inferred literal types if + // all inferences were made to top-level ocurrences of the type parameter, and + // the type parameter has no constraint or its constraint includes no primitive or literal types, and + // the type parameter was fixed during inference or does not occur at top-level in the return type. + const widenLiteralTypes = inference.topLevel && + !hasPrimitiveConstraint(inference.typeParameter) && + (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); + const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates; + // If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if + // union types were requested or if all inferences were made from the return type position, infer a + // union type. Otherwise, infer a common supertype. + const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? + getUnionType(baseCandidates, UnionReduction.Subtype) : + getCommonSupertype(baseCandidates); + inferredType = getWidenedType(unwidenedType); + // If we have inferred 'never' but have contravariant candidates. To get a more specific type we + // infer from the contravariant candidates instead. + if (inferredType.flags & TypeFlags.Never && inference.contraCandidates) { + inferredType = getCommonSubtype(inference.contraCandidates); + } + } + else if (inference.contraCandidates) { + // We only have contravariant inferences, infer the best common subtype of those inferredType = getCommonSubtype(inference.contraCandidates); } - } - else if (inference.contraCandidates) { - // We only have contravariant inferences, infer the best common subtype of those - inferredType = getCommonSubtype(inference.contraCandidates); - } - else if (context.flags & InferenceFlags.NoDefault) { - // We use silentNeverType as the wildcard that signals no inferences. - inferredType = silentNeverType; - } - else { - // Infer either the default or the empty object type when no inferences were - // made. It is important to remember that in this case, inference still - // succeeds, meaning there is no error for not having inference candidates. An - // inference error only occurs when there are *conflicting* candidates, i.e. - // candidates with no common supertype. - const defaultType = getDefaultFromTypeParameter(inference.typeParameter); - if (defaultType) { - // Instantiate the default type. Any forward reference to a type - // parameter should be instantiated to the empty object type. - inferredType = instantiateType(defaultType, - combineTypeMappers( - createBackreferenceMapper(context.signature.typeParameters, index), - context)); + else if (context.flags & InferenceFlags.NoDefault) { + // We use silentNeverType as the wildcard that signals no inferences. + inferredType = silentNeverType; } else { - inferredType = getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault)); + // Infer either the default or the empty object type when no inferences were + // made. It is important to remember that in this case, inference still + // succeeds, meaning there is no error for not having inference candidates. An + // inference error only occurs when there are *conflicting* candidates, i.e. + // candidates with no common supertype. + const defaultType = getDefaultFromTypeParameter(inference.typeParameter); + if (defaultType) { + // Instantiate the default type. Any forward reference to a type + // parameter should be instantiated to the empty object type. + inferredType = instantiateType(defaultType, + combineTypeMappers( + createBackreferenceMapper(context.signature.typeParameters, index), + context)); + } + else { + inferredType = getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault)); + } } } + else { + inferredType = getTypeFromInference(inference); + } inferredType = getWidenedUniqueESSymbolType(inferredType); inference.inferredType = inferredType; - const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]); + const constraint = getConstraintOfTypeParameter(inference.typeParameter); if (constraint) { const instantiatedConstraint = instantiateType(constraint, context); if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) { @@ -15404,7 +15407,7 @@ namespace ts { for (const signature of signatures) { if (signature.typeParameters) { const isJavascript = isInJavaScriptFile(node); - const inferenceContext = createInferenceContext(signature, /*flags*/ isJavascript ? InferenceFlags.AnyDefault : InferenceFlags.None); + const inferenceContext = createInferenceContext(signature.typeParameters, signature, /*flags*/ isJavascript ? InferenceFlags.AnyDefault : InferenceFlags.None); const typeArguments = inferJsxTypeArguments(signature, node, inferenceContext); instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript)); } @@ -16708,7 +16711,7 @@ namespace ts { // Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec) function instantiateSignatureInContextOf(signature: Signature, contextualSignature: Signature, contextualMapper?: TypeMapper, compareTypes?: TypeComparer): Signature { - const context = createInferenceContext(signature, InferenceFlags.InferUnionTypes, compareTypes); + const context = createInferenceContext(signature.typeParameters, signature, InferenceFlags.InferUnionTypes, compareTypes); forEachMatchingParameterType(contextualSignature, signature, (source, target) => { // Type parameters from outer context referenced by source type are fixed by instantiation of the source type inferTypes(context.inferences, instantiateType(source, contextualMapper || identityMapper), target); @@ -17474,7 +17477,7 @@ namespace ts { let candidate: Signature; const inferenceContext = originalCandidate.typeParameters ? - createInferenceContext(originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None) : + createInferenceContext(originalCandidate.typeParameters, originalCandidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None) : undefined; while (true) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b933d6853afb4..5e2bce4d72e87 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3966,7 +3966,8 @@ namespace ts { /* @internal */ export interface InferenceContext extends TypeMapper { - signature: Signature; // Generic signature for which inferences are made + typeParameters: TypeParameter[]; // Type parameters for which inferences are made + signature: Signature; // Generic signature for which inferences are made (if any) inferences: InferenceInfo[]; // Inferences made for each type parameter flags: InferenceFlags; // Inference flags compareTypes: TypeComparer; // Type comparer function diff --git a/tests/baselines/reference/inferTypes1.errors.txt b/tests/baselines/reference/inferTypes1.errors.txt index 5531ec08c7f39..3b627a0557f25 100644 --- a/tests/baselines/reference/inferTypes1.errors.txt +++ b/tests/baselines/reference/inferTypes1.errors.txt @@ -17,7 +17,7 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(75,43): error TS2304: C tests/cases/conformance/types/conditional/inferTypes1.ts(75,43): error TS4081: Exported type alias 'T62' has or is using private name 'U'. tests/cases/conformance/types/conditional/inferTypes1.ts(81,44): error TS2344: Type 'U' does not satisfy the constraint 'string'. Type 'number' is not assignable to type 'string'. -tests/cases/conformance/types/conditional/inferTypes1.ts(134,40): error TS2322: Type 'T' is not assignable to type 'string'. +tests/cases/conformance/types/conditional/inferTypes1.ts(143,40): error TS2322: Type 'T' is not assignable to type 'string'. ==== tests/cases/conformance/types/conditional/inferTypes1.ts (16 errors) ==== @@ -144,6 +144,15 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(134,40): error TS2322: type T77 = T extends T76 ? T76 : never; type T78 = T extends T76 ? T76 : never; + type Foo = [T, U]; + type Bar = T extends Foo ? Foo : never; + + type T90 = Bar<[string, string]>; // [string, string] + type T91 = Bar<[string, "a"]>; // [string, "a"] + type T92 = Bar<[string, "a"] & { x: string }>; // [string, "a"] + type T93 = Bar<["a", string]>; // never + type T94 = Bar<[number, number]>; // never + // Example from #21496 type JsonifiedObject = { [K in keyof T]: Jsonified }; @@ -206,4 +215,12 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(134,40): error TS2322: type T80 = MatchingKeys; type T81 = VoidKeys; + + // Repro from #22221 + + type MustBeString = T; + type EnsureIsString = T extends MustBeString ? U : never; + + type Test1 = EnsureIsString<"hello">; // "hello" + type Test2 = EnsureIsString<42>; // never \ No newline at end of file diff --git a/tests/baselines/reference/inferTypes1.js b/tests/baselines/reference/inferTypes1.js index 6e41cfe199844..909fbf2998241 100644 --- a/tests/baselines/reference/inferTypes1.js +++ b/tests/baselines/reference/inferTypes1.js @@ -88,6 +88,15 @@ type T76 = { x: T }; type T77 = T extends T76 ? T76 : never; type T78 = T extends T76 ? T76 : never; +type Foo = [T, U]; +type Bar = T extends Foo ? Foo : never; + +type T90 = Bar<[string, string]>; // [string, string] +type T91 = Bar<[string, "a"]>; // [string, "a"] +type T92 = Bar<[string, "a"] & { x: string }>; // [string, "a"] +type T93 = Bar<["a", string]>; // never +type T94 = Bar<[number, number]>; // never + // Example from #21496 type JsonifiedObject = { [K in keyof T]: Jsonified }; @@ -148,6 +157,14 @@ interface test { type T80 = MatchingKeys; type T81 = VoidKeys; + +// Repro from #22221 + +type MustBeString = T; +type EnsureIsString = T extends MustBeString ? U : never; + +type Test1 = EnsureIsString<"hello">; // "hello" +type Test2 = EnsureIsString<42>; // never //// [inferTypes1.js] diff --git a/tests/baselines/reference/inferTypes1.symbols b/tests/baselines/reference/inferTypes1.symbols index 9e09eaea21d51..b1e82a16f5ff0 100644 --- a/tests/baselines/reference/inferTypes1.symbols +++ b/tests/baselines/reference/inferTypes1.symbols @@ -406,216 +406,279 @@ type T78 = T extends T76 ? T76 : never; >X : Symbol(X, Decl(inferTypes1.ts, 87, 33), Decl(inferTypes1.ts, 87, 42)) >X : Symbol(X, Decl(inferTypes1.ts, 87, 33), Decl(inferTypes1.ts, 87, 42)) +type Foo = [T, U]; +>Foo : Symbol(Foo, Decl(inferTypes1.ts, 87, 66)) +>T : Symbol(T, Decl(inferTypes1.ts, 89, 9)) +>U : Symbol(U, Decl(inferTypes1.ts, 89, 26)) +>T : Symbol(T, Decl(inferTypes1.ts, 89, 9)) +>T : Symbol(T, Decl(inferTypes1.ts, 89, 9)) +>U : Symbol(U, Decl(inferTypes1.ts, 89, 26)) + +type Bar = T extends Foo ? Foo : never; +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) +>T : Symbol(T, Decl(inferTypes1.ts, 90, 9)) +>T : Symbol(T, Decl(inferTypes1.ts, 90, 9)) +>Foo : Symbol(Foo, Decl(inferTypes1.ts, 87, 66)) +>X : Symbol(X, Decl(inferTypes1.ts, 90, 33)) +>Y : Symbol(Y, Decl(inferTypes1.ts, 90, 42)) +>Foo : Symbol(Foo, Decl(inferTypes1.ts, 87, 66)) +>X : Symbol(X, Decl(inferTypes1.ts, 90, 33)) +>Y : Symbol(Y, Decl(inferTypes1.ts, 90, 42)) + +type T90 = Bar<[string, string]>; // [string, string] +>T90 : Symbol(T90, Decl(inferTypes1.ts, 90, 66)) +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) + +type T91 = Bar<[string, "a"]>; // [string, "a"] +>T91 : Symbol(T91, Decl(inferTypes1.ts, 92, 33)) +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) + +type T92 = Bar<[string, "a"] & { x: string }>; // [string, "a"] +>T92 : Symbol(T92, Decl(inferTypes1.ts, 93, 30)) +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) +>x : Symbol(x, Decl(inferTypes1.ts, 94, 32)) + +type T93 = Bar<["a", string]>; // never +>T93 : Symbol(T93, Decl(inferTypes1.ts, 94, 46)) +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) + +type T94 = Bar<[number, number]>; // never +>T94 : Symbol(T94, Decl(inferTypes1.ts, 95, 30)) +>Bar : Symbol(Bar, Decl(inferTypes1.ts, 89, 49)) + // Example from #21496 type JsonifiedObject = { [K in keyof T]: Jsonified }; ->JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 87, 66)) ->T : Symbol(T, Decl(inferTypes1.ts, 91, 21)) ->K : Symbol(K, Decl(inferTypes1.ts, 91, 44)) ->T : Symbol(T, Decl(inferTypes1.ts, 91, 21)) ->Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 91, 77)) ->T : Symbol(T, Decl(inferTypes1.ts, 91, 21)) ->K : Symbol(K, Decl(inferTypes1.ts, 91, 44)) +>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 96, 33)) +>T : Symbol(T, Decl(inferTypes1.ts, 100, 21)) +>K : Symbol(K, Decl(inferTypes1.ts, 100, 44)) +>T : Symbol(T, Decl(inferTypes1.ts, 100, 21)) +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 100, 77)) +>T : Symbol(T, Decl(inferTypes1.ts, 100, 21)) +>K : Symbol(K, Decl(inferTypes1.ts, 100, 44)) type Jsonified = ->Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 91, 77)) ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 100, 77)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) T extends string | number | boolean | null ? T ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) : T extends undefined | Function ? never // undefined and functions are removed ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) >Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) : T extends { toJSON(): infer R } ? R // toJSON is called if it exists (e.g. Date) ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) ->toJSON : Symbol(toJSON, Decl(inferTypes1.ts, 96, 17)) ->R : Symbol(R, Decl(inferTypes1.ts, 96, 33)) ->R : Symbol(R, Decl(inferTypes1.ts, 96, 33)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) +>toJSON : Symbol(toJSON, Decl(inferTypes1.ts, 105, 17)) +>R : Symbol(R, Decl(inferTypes1.ts, 105, 33)) +>R : Symbol(R, Decl(inferTypes1.ts, 105, 33)) : T extends object ? JsonifiedObject ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) ->JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 87, 66)) ->T : Symbol(T, Decl(inferTypes1.ts, 93, 15)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) +>JsonifiedObject : Symbol(JsonifiedObject, Decl(inferTypes1.ts, 96, 33)) +>T : Symbol(T, Decl(inferTypes1.ts, 102, 15)) : "what is this"; type Example = { ->Example : Symbol(Example, Decl(inferTypes1.ts, 98, 21)) +>Example : Symbol(Example, Decl(inferTypes1.ts, 107, 21)) str: "literalstring", ->str : Symbol(str, Decl(inferTypes1.ts, 100, 16)) +>str : Symbol(str, Decl(inferTypes1.ts, 109, 16)) fn: () => void, ->fn : Symbol(fn, Decl(inferTypes1.ts, 101, 25)) +>fn : Symbol(fn, Decl(inferTypes1.ts, 110, 25)) date: Date, ->date : Symbol(date, Decl(inferTypes1.ts, 102, 19)) +>date : Symbol(date, Decl(inferTypes1.ts, 111, 19)) >Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) customClass: MyClass, ->customClass : Symbol(customClass, Decl(inferTypes1.ts, 103, 15)) ->MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 110, 1)) +>customClass : Symbol(customClass, Decl(inferTypes1.ts, 112, 15)) +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 119, 1)) obj: { ->obj : Symbol(obj, Decl(inferTypes1.ts, 104, 25)) +>obj : Symbol(obj, Decl(inferTypes1.ts, 113, 25)) prop: "property", ->prop : Symbol(prop, Decl(inferTypes1.ts, 105, 10)) +>prop : Symbol(prop, Decl(inferTypes1.ts, 114, 10)) clz: MyClass, ->clz : Symbol(clz, Decl(inferTypes1.ts, 106, 25)) ->MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 110, 1)) +>clz : Symbol(clz, Decl(inferTypes1.ts, 115, 25)) +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 119, 1)) nested: { attr: Date } ->nested : Symbol(nested, Decl(inferTypes1.ts, 107, 21)) ->attr : Symbol(attr, Decl(inferTypes1.ts, 108, 17)) +>nested : Symbol(nested, Decl(inferTypes1.ts, 116, 21)) +>attr : Symbol(attr, Decl(inferTypes1.ts, 117, 17)) >Date : Symbol(Date, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) }, } declare class MyClass { ->MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 110, 1)) +>MyClass : Symbol(MyClass, Decl(inferTypes1.ts, 119, 1)) toJSON(): "correct"; ->toJSON : Symbol(MyClass.toJSON, Decl(inferTypes1.ts, 112, 23)) +>toJSON : Symbol(MyClass.toJSON, Decl(inferTypes1.ts, 121, 23)) } type JsonifiedExample = Jsonified; ->JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 114, 1)) ->Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 91, 77)) ->Example : Symbol(Example, Decl(inferTypes1.ts, 98, 21)) +>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 123, 1)) +>Jsonified : Symbol(Jsonified, Decl(inferTypes1.ts, 100, 77)) +>Example : Symbol(Example, Decl(inferTypes1.ts, 107, 21)) declare let ex: JsonifiedExample; ->ex : Symbol(ex, Decl(inferTypes1.ts, 117, 11)) ->JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 114, 1)) +>ex : Symbol(ex, Decl(inferTypes1.ts, 126, 11)) +>JsonifiedExample : Symbol(JsonifiedExample, Decl(inferTypes1.ts, 123, 1)) const z1: "correct" = ex.customClass; ->z1 : Symbol(z1, Decl(inferTypes1.ts, 118, 5)) ->ex.customClass : Symbol(customClass, Decl(inferTypes1.ts, 103, 15)) ->ex : Symbol(ex, Decl(inferTypes1.ts, 117, 11)) ->customClass : Symbol(customClass, Decl(inferTypes1.ts, 103, 15)) +>z1 : Symbol(z1, Decl(inferTypes1.ts, 127, 5)) +>ex.customClass : Symbol(customClass, Decl(inferTypes1.ts, 112, 15)) +>ex : Symbol(ex, Decl(inferTypes1.ts, 126, 11)) +>customClass : Symbol(customClass, Decl(inferTypes1.ts, 112, 15)) const z2: string = ex.obj.nested.attr; ->z2 : Symbol(z2, Decl(inferTypes1.ts, 119, 5)) ->ex.obj.nested.attr : Symbol(attr, Decl(inferTypes1.ts, 108, 17)) ->ex.obj.nested : Symbol(nested, Decl(inferTypes1.ts, 107, 21)) ->ex.obj : Symbol(obj, Decl(inferTypes1.ts, 104, 25)) ->ex : Symbol(ex, Decl(inferTypes1.ts, 117, 11)) ->obj : Symbol(obj, Decl(inferTypes1.ts, 104, 25)) ->nested : Symbol(nested, Decl(inferTypes1.ts, 107, 21)) ->attr : Symbol(attr, Decl(inferTypes1.ts, 108, 17)) +>z2 : Symbol(z2, Decl(inferTypes1.ts, 128, 5)) +>ex.obj.nested.attr : Symbol(attr, Decl(inferTypes1.ts, 117, 17)) +>ex.obj.nested : Symbol(nested, Decl(inferTypes1.ts, 116, 21)) +>ex.obj : Symbol(obj, Decl(inferTypes1.ts, 113, 25)) +>ex : Symbol(ex, Decl(inferTypes1.ts, 126, 11)) +>obj : Symbol(obj, Decl(inferTypes1.ts, 113, 25)) +>nested : Symbol(nested, Decl(inferTypes1.ts, 116, 21)) +>attr : Symbol(attr, Decl(inferTypes1.ts, 117, 17)) // Repros from #21631 type A1> = [T, U]; ->A1 : Symbol(A1, Decl(inferTypes1.ts, 119, 38)) ->T : Symbol(T, Decl(inferTypes1.ts, 123, 8)) ->U : Symbol(U, Decl(inferTypes1.ts, 123, 10)) ->A1 : Symbol(A1, Decl(inferTypes1.ts, 119, 38)) ->T : Symbol(T, Decl(inferTypes1.ts, 123, 8)) ->U : Symbol(U, Decl(inferTypes1.ts, 123, 10)) +>A1 : Symbol(A1, Decl(inferTypes1.ts, 128, 38)) +>T : Symbol(T, Decl(inferTypes1.ts, 132, 8)) +>U : Symbol(U, Decl(inferTypes1.ts, 132, 10)) +>A1 : Symbol(A1, Decl(inferTypes1.ts, 128, 38)) +>T : Symbol(T, Decl(inferTypes1.ts, 132, 8)) +>U : Symbol(U, Decl(inferTypes1.ts, 132, 10)) type B1 = S extends A1 ? [T, U] : never; ->B1 : Symbol(B1, Decl(inferTypes1.ts, 123, 44)) ->S : Symbol(S, Decl(inferTypes1.ts, 124, 8)) ->S : Symbol(S, Decl(inferTypes1.ts, 124, 8)) ->A1 : Symbol(A1, Decl(inferTypes1.ts, 119, 38)) ->T : Symbol(T, Decl(inferTypes1.ts, 124, 31)) ->U : Symbol(U, Decl(inferTypes1.ts, 124, 40)) ->T : Symbol(T, Decl(inferTypes1.ts, 124, 31)) ->U : Symbol(U, Decl(inferTypes1.ts, 124, 40)) +>B1 : Symbol(B1, Decl(inferTypes1.ts, 132, 44)) +>S : Symbol(S, Decl(inferTypes1.ts, 133, 8)) +>S : Symbol(S, Decl(inferTypes1.ts, 133, 8)) +>A1 : Symbol(A1, Decl(inferTypes1.ts, 128, 38)) +>T : Symbol(T, Decl(inferTypes1.ts, 133, 31)) +>U : Symbol(U, Decl(inferTypes1.ts, 133, 40)) +>T : Symbol(T, Decl(inferTypes1.ts, 133, 31)) +>U : Symbol(U, Decl(inferTypes1.ts, 133, 40)) type A2 = [T, U]; ->A2 : Symbol(A2, Decl(inferTypes1.ts, 124, 61)) ->T : Symbol(T, Decl(inferTypes1.ts, 126, 8)) ->U : Symbol(U, Decl(inferTypes1.ts, 126, 10)) ->T : Symbol(T, Decl(inferTypes1.ts, 126, 8)) ->U : Symbol(U, Decl(inferTypes1.ts, 126, 10)) +>A2 : Symbol(A2, Decl(inferTypes1.ts, 133, 61)) +>T : Symbol(T, Decl(inferTypes1.ts, 135, 8)) +>U : Symbol(U, Decl(inferTypes1.ts, 135, 10)) +>T : Symbol(T, Decl(inferTypes1.ts, 135, 8)) +>U : Symbol(U, Decl(inferTypes1.ts, 135, 10)) type B2 = S extends A2 ? [T, U] : never; ->B2 : Symbol(B2, Decl(inferTypes1.ts, 126, 36)) ->S : Symbol(S, Decl(inferTypes1.ts, 127, 8)) ->S : Symbol(S, Decl(inferTypes1.ts, 127, 8)) ->A2 : Symbol(A2, Decl(inferTypes1.ts, 124, 61)) ->T : Symbol(T, Decl(inferTypes1.ts, 127, 31)) ->U : Symbol(U, Decl(inferTypes1.ts, 127, 40)) ->T : Symbol(T, Decl(inferTypes1.ts, 127, 31)) ->U : Symbol(U, Decl(inferTypes1.ts, 127, 40)) +>B2 : Symbol(B2, Decl(inferTypes1.ts, 135, 36)) +>S : Symbol(S, Decl(inferTypes1.ts, 136, 8)) +>S : Symbol(S, Decl(inferTypes1.ts, 136, 8)) +>A2 : Symbol(A2, Decl(inferTypes1.ts, 133, 61)) +>T : Symbol(T, Decl(inferTypes1.ts, 136, 31)) +>U : Symbol(U, Decl(inferTypes1.ts, 136, 40)) +>T : Symbol(T, Decl(inferTypes1.ts, 136, 31)) +>U : Symbol(U, Decl(inferTypes1.ts, 136, 40)) type C2 = S extends A2 ? [T, U] : never; ->C2 : Symbol(C2, Decl(inferTypes1.ts, 127, 61)) ->S : Symbol(S, Decl(inferTypes1.ts, 128, 8)) ->U : Symbol(U, Decl(inferTypes1.ts, 128, 10)) ->S : Symbol(S, Decl(inferTypes1.ts, 128, 8)) ->A2 : Symbol(A2, Decl(inferTypes1.ts, 124, 61)) ->T : Symbol(T, Decl(inferTypes1.ts, 128, 47)) ->U : Symbol(U, Decl(inferTypes1.ts, 128, 10)) ->T : Symbol(T, Decl(inferTypes1.ts, 128, 47)) ->U : Symbol(U, Decl(inferTypes1.ts, 128, 10)) +>C2 : Symbol(C2, Decl(inferTypes1.ts, 136, 61)) +>S : Symbol(S, Decl(inferTypes1.ts, 137, 8)) +>U : Symbol(U, Decl(inferTypes1.ts, 137, 10)) +>S : Symbol(S, Decl(inferTypes1.ts, 137, 8)) +>A2 : Symbol(A2, Decl(inferTypes1.ts, 133, 61)) +>T : Symbol(T, Decl(inferTypes1.ts, 137, 47)) +>U : Symbol(U, Decl(inferTypes1.ts, 137, 10)) +>T : Symbol(T, Decl(inferTypes1.ts, 137, 47)) +>U : Symbol(U, Decl(inferTypes1.ts, 137, 10)) // Repro from #21735 type A = T extends string ? { [P in T]: void; } : T; ->A : Symbol(A, Decl(inferTypes1.ts, 128, 71)) ->T : Symbol(T, Decl(inferTypes1.ts, 132, 7)) ->T : Symbol(T, Decl(inferTypes1.ts, 132, 7)) ->P : Symbol(P, Decl(inferTypes1.ts, 132, 34)) ->T : Symbol(T, Decl(inferTypes1.ts, 132, 7)) ->T : Symbol(T, Decl(inferTypes1.ts, 132, 7)) +>A : Symbol(A, Decl(inferTypes1.ts, 137, 71)) +>T : Symbol(T, Decl(inferTypes1.ts, 141, 7)) +>T : Symbol(T, Decl(inferTypes1.ts, 141, 7)) +>P : Symbol(P, Decl(inferTypes1.ts, 141, 34)) +>T : Symbol(T, Decl(inferTypes1.ts, 141, 7)) +>T : Symbol(T, Decl(inferTypes1.ts, 141, 7)) type B = string extends T ? { [P in T]: void; } : T; // Error ->B : Symbol(B, Decl(inferTypes1.ts, 132, 55)) ->T : Symbol(T, Decl(inferTypes1.ts, 133, 7)) ->T : Symbol(T, Decl(inferTypes1.ts, 133, 7)) ->P : Symbol(P, Decl(inferTypes1.ts, 133, 34)) ->T : Symbol(T, Decl(inferTypes1.ts, 133, 7)) ->T : Symbol(T, Decl(inferTypes1.ts, 133, 7)) +>B : Symbol(B, Decl(inferTypes1.ts, 141, 55)) +>T : Symbol(T, Decl(inferTypes1.ts, 142, 7)) +>T : Symbol(T, Decl(inferTypes1.ts, 142, 7)) +>P : Symbol(P, Decl(inferTypes1.ts, 142, 34)) +>T : Symbol(T, Decl(inferTypes1.ts, 142, 7)) +>T : Symbol(T, Decl(inferTypes1.ts, 142, 7)) // Repro from #22302 type MatchingKeys = ->MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 133, 55)) ->T : Symbol(T, Decl(inferTypes1.ts, 137, 18)) ->U : Symbol(U, Decl(inferTypes1.ts, 137, 20)) ->K : Symbol(K, Decl(inferTypes1.ts, 137, 23)) ->T : Symbol(T, Decl(inferTypes1.ts, 137, 18)) ->T : Symbol(T, Decl(inferTypes1.ts, 137, 18)) +>MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 142, 55)) +>T : Symbol(T, Decl(inferTypes1.ts, 146, 18)) +>U : Symbol(U, Decl(inferTypes1.ts, 146, 20)) +>K : Symbol(K, Decl(inferTypes1.ts, 146, 23)) +>T : Symbol(T, Decl(inferTypes1.ts, 146, 18)) +>T : Symbol(T, Decl(inferTypes1.ts, 146, 18)) K extends keyof T ? T[K] extends U ? K : never : never; ->K : Symbol(K, Decl(inferTypes1.ts, 137, 23)) ->T : Symbol(T, Decl(inferTypes1.ts, 137, 18)) ->T : Symbol(T, Decl(inferTypes1.ts, 137, 18)) ->K : Symbol(K, Decl(inferTypes1.ts, 137, 23)) ->U : Symbol(U, Decl(inferTypes1.ts, 137, 20)) ->K : Symbol(K, Decl(inferTypes1.ts, 137, 23)) +>K : Symbol(K, Decl(inferTypes1.ts, 146, 23)) +>T : Symbol(T, Decl(inferTypes1.ts, 146, 18)) +>T : Symbol(T, Decl(inferTypes1.ts, 146, 18)) +>K : Symbol(K, Decl(inferTypes1.ts, 146, 23)) +>U : Symbol(U, Decl(inferTypes1.ts, 146, 20)) +>K : Symbol(K, Decl(inferTypes1.ts, 146, 23)) type VoidKeys = MatchingKeys; ->VoidKeys : Symbol(VoidKeys, Decl(inferTypes1.ts, 138, 59)) ->T : Symbol(T, Decl(inferTypes1.ts, 140, 14)) ->MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 133, 55)) ->T : Symbol(T, Decl(inferTypes1.ts, 140, 14)) +>VoidKeys : Symbol(VoidKeys, Decl(inferTypes1.ts, 147, 59)) +>T : Symbol(T, Decl(inferTypes1.ts, 149, 14)) +>MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 142, 55)) +>T : Symbol(T, Decl(inferTypes1.ts, 149, 14)) interface test { ->test : Symbol(test, Decl(inferTypes1.ts, 140, 41)) +>test : Symbol(test, Decl(inferTypes1.ts, 149, 41)) a: 1, ->a : Symbol(test.a, Decl(inferTypes1.ts, 142, 16)) +>a : Symbol(test.a, Decl(inferTypes1.ts, 151, 16)) b: void ->b : Symbol(test.b, Decl(inferTypes1.ts, 143, 9)) +>b : Symbol(test.b, Decl(inferTypes1.ts, 152, 9)) } type T80 = MatchingKeys; ->T80 : Symbol(T80, Decl(inferTypes1.ts, 145, 1)) ->MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 133, 55)) ->test : Symbol(test, Decl(inferTypes1.ts, 140, 41)) +>T80 : Symbol(T80, Decl(inferTypes1.ts, 154, 1)) +>MatchingKeys : Symbol(MatchingKeys, Decl(inferTypes1.ts, 142, 55)) +>test : Symbol(test, Decl(inferTypes1.ts, 149, 41)) type T81 = VoidKeys; ->T81 : Symbol(T81, Decl(inferTypes1.ts, 147, 36)) ->VoidKeys : Symbol(VoidKeys, Decl(inferTypes1.ts, 138, 59)) ->test : Symbol(test, Decl(inferTypes1.ts, 140, 41)) +>T81 : Symbol(T81, Decl(inferTypes1.ts, 156, 36)) +>VoidKeys : Symbol(VoidKeys, Decl(inferTypes1.ts, 147, 59)) +>test : Symbol(test, Decl(inferTypes1.ts, 149, 41)) + +// Repro from #22221 + +type MustBeString = T; +>MustBeString : Symbol(MustBeString, Decl(inferTypes1.ts, 157, 26)) +>T : Symbol(T, Decl(inferTypes1.ts, 161, 18)) +>T : Symbol(T, Decl(inferTypes1.ts, 161, 18)) + +type EnsureIsString = T extends MustBeString ? U : never; +>EnsureIsString : Symbol(EnsureIsString, Decl(inferTypes1.ts, 161, 40)) +>T : Symbol(T, Decl(inferTypes1.ts, 162, 20)) +>T : Symbol(T, Decl(inferTypes1.ts, 162, 20)) +>MustBeString : Symbol(MustBeString, Decl(inferTypes1.ts, 157, 26)) +>U : Symbol(U, Decl(inferTypes1.ts, 162, 53)) +>U : Symbol(U, Decl(inferTypes1.ts, 162, 53)) + +type Test1 = EnsureIsString<"hello">; // "hello" +>Test1 : Symbol(Test1, Decl(inferTypes1.ts, 162, 69)) +>EnsureIsString : Symbol(EnsureIsString, Decl(inferTypes1.ts, 161, 40)) + +type Test2 = EnsureIsString<42>; // never +>Test2 : Symbol(Test2, Decl(inferTypes1.ts, 164, 37)) +>EnsureIsString : Symbol(EnsureIsString, Decl(inferTypes1.ts, 161, 40)) diff --git a/tests/baselines/reference/inferTypes1.types b/tests/baselines/reference/inferTypes1.types index 523dd669e4cc1..1d0a48c5169a3 100644 --- a/tests/baselines/reference/inferTypes1.types +++ b/tests/baselines/reference/inferTypes1.types @@ -412,6 +412,46 @@ type T78 = T extends T76 ? T76 : never; >X : X >X : X +type Foo = [T, U]; +>Foo : [T, U] +>T : T +>U : U +>T : T +>T : T +>U : U + +type Bar = T extends Foo ? Foo : never; +>Bar : Bar +>T : T +>T : T +>Foo : [T, U] +>X : X +>Y : Y +>Foo : [T, U] +>X : X +>Y : Y + +type T90 = Bar<[string, string]>; // [string, string] +>T90 : [string, string] +>Bar : Bar + +type T91 = Bar<[string, "a"]>; // [string, "a"] +>T91 : [string, "a"] +>Bar : Bar + +type T92 = Bar<[string, "a"] & { x: string }>; // [string, "a"] +>T92 : [string, "a"] +>Bar : Bar +>x : string + +type T93 = Bar<["a", string]>; // never +>T93 : never +>Bar : Bar + +type T94 = Bar<[number, number]>; // never +>T94 : never +>Bar : Bar + // Example from #21496 type JsonifiedObject = { [K in keyof T]: Jsonified }; @@ -626,3 +666,26 @@ type T81 = VoidKeys; >VoidKeys : MatchingKeys >test : test +// Repro from #22221 + +type MustBeString = T; +>MustBeString : T +>T : T +>T : T + +type EnsureIsString = T extends MustBeString ? U : never; +>EnsureIsString : EnsureIsString +>T : T +>T : T +>MustBeString : T +>U : U +>U : U + +type Test1 = EnsureIsString<"hello">; // "hello" +>Test1 : "hello" +>EnsureIsString : EnsureIsString + +type Test2 = EnsureIsString<42>; // never +>Test2 : never +>EnsureIsString : EnsureIsString + diff --git a/tests/cases/conformance/types/conditional/inferTypes1.ts b/tests/cases/conformance/types/conditional/inferTypes1.ts index 1defacc08cc5b..8907cf79d9137 100644 --- a/tests/cases/conformance/types/conditional/inferTypes1.ts +++ b/tests/cases/conformance/types/conditional/inferTypes1.ts @@ -90,6 +90,15 @@ type T76 = { x: T }; type T77 = T extends T76 ? T76 : never; type T78 = T extends T76 ? T76 : never; +type Foo = [T, U]; +type Bar = T extends Foo ? Foo : never; + +type T90 = Bar<[string, string]>; // [string, string] +type T91 = Bar<[string, "a"]>; // [string, "a"] +type T92 = Bar<[string, "a"] & { x: string }>; // [string, "a"] +type T93 = Bar<["a", string]>; // never +type T94 = Bar<[number, number]>; // never + // Example from #21496 type JsonifiedObject = { [K in keyof T]: Jsonified }; @@ -150,3 +159,11 @@ interface test { type T80 = MatchingKeys; type T81 = VoidKeys; + +// Repro from #22221 + +type MustBeString = T; +type EnsureIsString = T extends MustBeString ? U : never; + +type Test1 = EnsureIsString<"hello">; // "hello" +type Test2 = EnsureIsString<42>; // never