From 34515759c014eecda74aab16a465c2121c3f51b7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 17 Nov 2017 20:36:43 -0800 Subject: [PATCH 01/18] Infer type parameters from indexes on those parameters --- src/compiler/checker.ts | 90 ++++++++++++++----- src/compiler/types.ts | 4 + .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + .../reference/indexAccessCombinedInference.js | 29 ++++++ .../indexAccessCombinedInference.symbols | 46 ++++++++++ .../indexAccessCombinedInference.types | 54 +++++++++++ .../baselines/reference/inferingFromAny.types | 2 +- .../compiler/indexAccessCombinedInference.ts | 17 ++++ 9 files changed, 221 insertions(+), 23 deletions(-) create mode 100644 tests/baselines/reference/indexAccessCombinedInference.js create mode 100644 tests/baselines/reference/indexAccessCombinedInference.symbols create mode 100644 tests/baselines/reference/indexAccessCombinedInference.types create mode 100644 tests/cases/compiler/indexAccessCombinedInference.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 87d95b64d31cb..60d817623eaed 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2645,8 +2645,8 @@ namespace ts { function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; - const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; + const readonlyToken = isReadonlyMappedType(type) ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = isOptionalMappedType(type) ? createToken(SyntaxKind.QuestionToken) : undefined; const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context); const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context); @@ -3717,7 +3717,7 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); - if (type.declaration.readonlyToken) { + if (isReadonlyMappedType(type)) { writeKeyword(writer, SyntaxKind.ReadonlyKeyword); writeSpace(writer); } @@ -3728,7 +3728,7 @@ namespace ts { writeSpace(writer); writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None); writePunctuation(writer, SyntaxKind.CloseBracketToken); - if (type.declaration.questionToken) { + if (isOptionalMappedType(type)) { writePunctuation(writer, SyntaxKind.QuestionToken); } writePunctuation(writer, SyntaxKind.ColonToken); @@ -6108,11 +6108,9 @@ namespace ts { const constraintType = getConstraintTypeFromMappedType(type); const templateType = getTemplateTypeFromMappedType(type); const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' - const templateReadonly = !!type.declaration.readonlyToken; - const templateOptional = !!type.declaration.questionToken; - const constraintDeclaration = type.declaration.typeParameter.constraint; - if (constraintDeclaration.kind === SyntaxKind.TypeOperator && - (constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) { + const templateReadonly = isReadonlyMappedType(type); + const templateOptional = isOptionalMappedType(type); + if (isPossiblyHomomorphicMappedType(type)) { // We have a { [P in keyof T]: X } for (const propertySymbol of getPropertiesOfType(modifiersType)) { addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol); @@ -6180,15 +6178,14 @@ namespace ts { function getTemplateTypeFromMappedType(type: MappedType) { return type.templateType || (type.templateType = type.declaration.type ? - instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) : + instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), isOptionalMappedType(type)), type.mapper || identityMapper) : unknownType); } function getModifiersTypeFromMappedType(type: MappedType) { if (!type.modifiersType) { - const constraintDeclaration = type.declaration.typeParameter.constraint; - if (constraintDeclaration.kind === SyntaxKind.TypeOperator && - (constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) { + if (isPossiblyHomomorphicMappedType(type)) { + const constraintDeclaration = type.declaration.typeParameter.constraint; // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves // 'keyof T' to a literal union type and we can't recover T from that type. @@ -6208,8 +6205,8 @@ namespace ts { } function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers { - return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) | - (type.declaration.questionToken ? MappedTypeModifiers.Optional : 0); + return (isReadonlyMappedType(type) ? MappedTypeModifiers.Readonly : 0) | + (isOptionalMappedType(type) ? MappedTypeModifiers.Optional : 0); } function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers { @@ -6219,7 +6216,7 @@ namespace ts { } function isPartialMappedType(type: Type) { - return getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken; + return getObjectFlags(type) & ObjectFlags.Mapped && isOptionalMappedType(type as MappedType); } function isGenericMappedType(type: Type) { @@ -9885,7 +9882,7 @@ namespace ts { if (target.flags & TypeFlags.TypeParameter) { // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P]. if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(source) === getIndexType(target)) { - if (!(source).declaration.questionToken) { + if (!isOptionalMappedType(source)) { const templateType = getTemplateTypeFromMappedType(source); const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(source)); if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) { @@ -11197,7 +11194,8 @@ namespace ts { inferredType: undefined, priority: undefined, topLevel: true, - isFixed: false + isFixed: false, + indexes: undefined, }; } @@ -11208,7 +11206,8 @@ namespace ts { inferredType: inference.inferredType, priority: inference.priority, topLevel: inference.topLevel, - isFixed: inference.isFixed + isFixed: inference.isFixed, + indexes: inference.indexes && inference.indexes.slice(), }; } @@ -11271,8 +11270,8 @@ namespace ts { const inference = createInferenceInfo(typeParameter); const inferences = [inference]; const templateType = getTemplateTypeFromMappedType(target); - const readonlyMask = target.declaration.readonlyToken ? false : true; - const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional; + const readonlyMask = isReadonlyMappedType(target) ? false : true; + const optionalMask = isOptionalMappedType(target) ? 0 : SymbolFlags.Optional; const members = createSymbolTable(); for (const prop of properties) { const propType = getTypeOfSymbol(prop); @@ -11399,6 +11398,27 @@ namespace ts { } return; } + else if (target.flags & TypeFlags.IndexedAccess) { + const targetConstraint = (target).objectType; + const inference = getInferenceInfoForType(targetConstraint); + if (inference) { + if (!inference.isFixed) { + const map = createObjectType(ObjectFlags.Mapped); + map.templateType = source; + map.constraintType = (target).indexType; + map.typeParameter = createType(TypeFlags.TypeParameter); + // TODO (weswigham): Ensure the name chosen for the unused "K" does not shadow any other type variables in the given scope, so as to not have a chance of breaking declaration emit + map.typeParameter.symbol = createSymbol(SymbolFlags.TypeParameter, "K" as __String); + map.typeParameter.constraint = map.constraintType; + map.modifiersType = (target).indexType; + map.hasQuestionToken = false; + map.hasReadonlyToken = false; + map.hasPossiblyHomomorphicConstraint = false; + (inference.indexes || (inference.indexes = [])).push(map); + } + return; + } + } } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments @@ -11665,6 +11685,10 @@ namespace ts { const inference = context.inferences[index]; let inferredType = inference.inferredType; if (!inferredType) { + if (inference.indexes) { + // Build a candidate from all indexes + (inference.candidates || (inference.candidates = [])).push(getIntersectionType(inference.indexes)); + } if (inference.candidates) { // Extract all object literal types and replace them with a single widened and normalized type. const candidates = widenObjectLiteralCandidates(inference.candidates); @@ -20088,6 +20112,28 @@ namespace ts { forEach(node.types, checkSourceElement); } + function isReadonlyMappedType(type: MappedType) { + if (type.hasReadonlyToken === undefined) { + type.hasReadonlyToken = !!type.declaration.readonlyToken; + } + return type.hasReadonlyToken; + } + + function isOptionalMappedType(type: MappedType) { + if (type.hasQuestionToken === undefined) { + type.hasQuestionToken = !!type.declaration.questionToken; + } + return type.hasQuestionToken; + } + + function isPossiblyHomomorphicMappedType(type: MappedType) { + if (type.hasPossiblyHomomorphicConstraint === undefined) { + const constraint = type.declaration.typeParameter.constraint; + type.hasPossiblyHomomorphicConstraint = isTypeOperatorNode(constraint) && constraint.operator === SyntaxKind.KeyOfKeyword; + } + return type.hasPossiblyHomomorphicConstraint; + } + function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) { if (!(type.flags & TypeFlags.IndexedAccess)) { return type; @@ -20097,7 +20143,7 @@ namespace ts { const indexType = (type).indexType; if (isTypeAssignableTo(indexType, getIndexType(objectType))) { if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && - getObjectFlags(objectType) & ObjectFlags.Mapped && (objectType).declaration.readonlyToken) { + getObjectFlags(objectType) & ObjectFlags.Mapped && isReadonlyMappedType(objectType)) { error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); } return type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dfd6831be42f7..890746dc27636 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3594,6 +3594,9 @@ namespace ts { constraintType?: Type; templateType?: Type; modifiersType?: Type; + hasQuestionToken?: boolean; + hasReadonlyToken?: boolean; + hasPossiblyHomomorphicConstraint?: boolean; } export interface EvolvingArrayType extends ObjectType { @@ -3744,6 +3747,7 @@ namespace ts { export interface InferenceInfo { typeParameter: TypeParameter; candidates: Type[]; + indexes: Type[]; // Partial candidates created by indexed accesses inferredType: Type; priority: InferencePriority; topLevel: boolean; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ad34924a097f9..c566c2df83734 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2154,6 +2154,7 @@ declare namespace ts { interface InferenceInfo { typeParameter: TypeParameter; candidates: Type[]; + indexes: Type[]; inferredType: Type; priority: InferencePriority; topLevel: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 4d06e48dc2565..fefe085fe11db 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2154,6 +2154,7 @@ declare namespace ts { interface InferenceInfo { typeParameter: TypeParameter; candidates: Type[]; + indexes: Type[]; inferredType: Type; priority: InferencePriority; topLevel: boolean; diff --git a/tests/baselines/reference/indexAccessCombinedInference.js b/tests/baselines/reference/indexAccessCombinedInference.js new file mode 100644 index 0000000000000..a3f19fa7fcc67 --- /dev/null +++ b/tests/baselines/reference/indexAccessCombinedInference.js @@ -0,0 +1,29 @@ +//// [indexAccessCombinedInference.ts] +interface Args { + TA: object, + TY: object +} + +function foo( + a: T["TA"], + b: T["TY"]): T["TA"] & T["TY"] { + return undefined!; +} + +const x = foo({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); + +//// [indexAccessCombinedInference.js] +function foo(a, b) { + return undefined; +} +var x = foo({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); diff --git a/tests/baselines/reference/indexAccessCombinedInference.symbols b/tests/baselines/reference/indexAccessCombinedInference.symbols new file mode 100644 index 0000000000000..c422efe422c8c --- /dev/null +++ b/tests/baselines/reference/indexAccessCombinedInference.symbols @@ -0,0 +1,46 @@ +=== tests/cases/compiler/indexAccessCombinedInference.ts === +interface Args { +>Args : Symbol(Args, Decl(indexAccessCombinedInference.ts, 0, 0)) + + TA: object, +>TA : Symbol(Args.TA, Decl(indexAccessCombinedInference.ts, 0, 16)) + + TY: object +>TY : Symbol(Args.TY, Decl(indexAccessCombinedInference.ts, 1, 15)) +} + +function foo( +>foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 3, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) +>Args : Symbol(Args, Decl(indexAccessCombinedInference.ts, 0, 0)) + + a: T["TA"], +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 5, 29)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) + + b: T["TY"]): T["TA"] & T["TY"] { +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 6, 15)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) + + return undefined!; +>undefined : Symbol(undefined) +} + +const x = foo({ +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 11, 5)) +>foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 3, 1)) + + x: { +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 11, 15)) + + j: 12, +>j : Symbol(j, Decl(indexAccessCombinedInference.ts, 12, 8)) + + i: 11 +>i : Symbol(i, Decl(indexAccessCombinedInference.ts, 13, 14)) + } +}, { y: 42 }); +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 16, 4)) + diff --git a/tests/baselines/reference/indexAccessCombinedInference.types b/tests/baselines/reference/indexAccessCombinedInference.types new file mode 100644 index 0000000000000..f32c28de6266f --- /dev/null +++ b/tests/baselines/reference/indexAccessCombinedInference.types @@ -0,0 +1,54 @@ +=== tests/cases/compiler/indexAccessCombinedInference.ts === +interface Args { +>Args : Args + + TA: object, +>TA : object + + TY: object +>TY : object +} + +function foo( +>foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] +>T : T +>Args : Args + + a: T["TA"], +>a : T["TA"] +>T : T + + b: T["TY"]): T["TA"] & T["TY"] { +>b : T["TY"] +>T : T +>T : T +>T : T + + return undefined!; +>undefined! : undefined +>undefined : undefined +} + +const x = foo({ +>x : { x: { j: number; i: number; }; } & { y: number; } +>foo({ x: { j: 12, i: 11 }}, { y: 42 }) : { x: { j: number; i: number; }; } & { y: number; } +>foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] +>{ x: { j: 12, i: 11 }} : { x: { j: number; i: number; }; } + + x: { +>x : { j: number; i: number; } +>{ j: 12, i: 11 } : { j: number; i: number; } + + j: 12, +>j : number +>12 : 12 + + i: 11 +>i : number +>11 : 11 + } +}, { y: 42 }); +>{ y: 42 } : { y: number; } +>y : number +>42 : 42 + diff --git a/tests/baselines/reference/inferingFromAny.types b/tests/baselines/reference/inferingFromAny.types index 72e2024d0224e..dc660cdebc17d 100644 --- a/tests/baselines/reference/inferingFromAny.types +++ b/tests/baselines/reference/inferingFromAny.types @@ -293,7 +293,7 @@ var a = f18(a); var a = f19(a, a); >a : any ->f19(a, a) : any +>f19(a, a) : { [K in K]: any; } >f19 : (k: K, x: T[K]) => T >a : any >a : any diff --git a/tests/cases/compiler/indexAccessCombinedInference.ts b/tests/cases/compiler/indexAccessCombinedInference.ts new file mode 100644 index 0000000000000..c1d7f03ac22ee --- /dev/null +++ b/tests/cases/compiler/indexAccessCombinedInference.ts @@ -0,0 +1,17 @@ +interface Args { + TA: object, + TY: object +} + +function foo( + a: T["TA"], + b: T["TY"]): T["TA"] & T["TY"] { + return undefined!; +} + +const x = foo({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); \ No newline at end of file From f5b208c23f20125dad1d5ba1ffc38cac1c0a7e4b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 21 Nov 2017 16:57:34 -0800 Subject: [PATCH 02/18] Greatly simplify partial inference type instantiation through use of an alias in lib --- src/compiler/checker.ts | 79 +++++++------------ src/lib/es5.d.ts | 9 +++ .../baselines/reference/inferingFromAny.types | 2 +- 3 files changed, 38 insertions(+), 52 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 60d817623eaed..d5e27dbeba82d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2645,8 +2645,8 @@ namespace ts { function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const readonlyToken = isReadonlyMappedType(type) ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; - const questionToken = isOptionalMappedType(type) ? createToken(SyntaxKind.QuestionToken) : undefined; + const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context); const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context); @@ -3717,7 +3717,7 @@ namespace ts { writePunctuation(writer, SyntaxKind.OpenBraceToken); writer.writeLine(); writer.increaseIndent(); - if (isReadonlyMappedType(type)) { + if (type.declaration.readonlyToken) { writeKeyword(writer, SyntaxKind.ReadonlyKeyword); writeSpace(writer); } @@ -3728,7 +3728,7 @@ namespace ts { writeSpace(writer); writeType(getConstraintTypeFromMappedType(type), TypeFormatFlags.None); writePunctuation(writer, SyntaxKind.CloseBracketToken); - if (isOptionalMappedType(type)) { + if (type.declaration.questionToken) { writePunctuation(writer, SyntaxKind.QuestionToken); } writePunctuation(writer, SyntaxKind.ColonToken); @@ -6108,9 +6108,11 @@ namespace ts { const constraintType = getConstraintTypeFromMappedType(type); const templateType = getTemplateTypeFromMappedType(type); const modifiersType = getApparentType(getModifiersTypeFromMappedType(type)); // The 'T' in 'keyof T' - const templateReadonly = isReadonlyMappedType(type); - const templateOptional = isOptionalMappedType(type); - if (isPossiblyHomomorphicMappedType(type)) { + const templateReadonly = !!type.declaration.readonlyToken; + const templateOptional = !!type.declaration.questionToken; + const constraintDeclaration = type.declaration.typeParameter.constraint; + if (constraintDeclaration.kind === SyntaxKind.TypeOperator && + (constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) { // We have a { [P in keyof T]: X } for (const propertySymbol of getPropertiesOfType(modifiersType)) { addMemberForKeyType(getLiteralTypeFromPropertyName(propertySymbol), propertySymbol); @@ -6178,14 +6180,15 @@ namespace ts { function getTemplateTypeFromMappedType(type: MappedType) { return type.templateType || (type.templateType = type.declaration.type ? - instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), isOptionalMappedType(type)), type.mapper || identityMapper) : + instantiateType(addOptionality(getTypeFromTypeNode(type.declaration.type), !!type.declaration.questionToken), type.mapper || identityMapper) : unknownType); } function getModifiersTypeFromMappedType(type: MappedType) { if (!type.modifiersType) { - if (isPossiblyHomomorphicMappedType(type)) { - const constraintDeclaration = type.declaration.typeParameter.constraint; + const constraintDeclaration = type.declaration.typeParameter.constraint; + if (constraintDeclaration.kind === SyntaxKind.TypeOperator && + (constraintDeclaration).operator === SyntaxKind.KeyOfKeyword) { // If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check // AST nodes here because, when T is a non-generic type, the logic below eagerly resolves // 'keyof T' to a literal union type and we can't recover T from that type. @@ -6205,8 +6208,8 @@ namespace ts { } function getMappedTypeModifiers(type: MappedType): MappedTypeModifiers { - return (isReadonlyMappedType(type) ? MappedTypeModifiers.Readonly : 0) | - (isOptionalMappedType(type) ? MappedTypeModifiers.Optional : 0); + return (type.declaration.readonlyToken ? MappedTypeModifiers.Readonly : 0) | + (type.declaration.questionToken ? MappedTypeModifiers.Optional : 0); } function getCombinedMappedTypeModifiers(type: MappedType): MappedTypeModifiers { @@ -6216,7 +6219,7 @@ namespace ts { } function isPartialMappedType(type: Type) { - return getObjectFlags(type) & ObjectFlags.Mapped && isOptionalMappedType(type as MappedType); + return getObjectFlags(type) & ObjectFlags.Mapped && !!(type).declaration.questionToken; } function isGenericMappedType(type: Type) { @@ -9882,7 +9885,7 @@ namespace ts { if (target.flags & TypeFlags.TypeParameter) { // A source type { [P in keyof T]: X } is related to a target type T if X is related to T[P]. if (getObjectFlags(source) & ObjectFlags.Mapped && getConstraintTypeFromMappedType(source) === getIndexType(target)) { - if (!isOptionalMappedType(source)) { + if (!(source).declaration.questionToken) { const templateType = getTemplateTypeFromMappedType(source); const indexedAccessType = getIndexedAccessType(target, getTypeParameterFromMappedType(source)); if (result = isRelatedTo(templateType, indexedAccessType, reportErrors)) { @@ -11270,8 +11273,8 @@ namespace ts { const inference = createInferenceInfo(typeParameter); const inferences = [inference]; const templateType = getTemplateTypeFromMappedType(target); - const readonlyMask = isReadonlyMappedType(target) ? false : true; - const optionalMask = isOptionalMappedType(target) ? 0 : SymbolFlags.Optional; + const readonlyMask = target.declaration.readonlyToken ? false : true; + const optionalMask = target.declaration.questionToken ? 0 : SymbolFlags.Optional; const members = createSymbolTable(); for (const prop of properties) { const propType = getTypeOfSymbol(prop); @@ -11403,18 +11406,14 @@ namespace ts { const inference = getInferenceInfoForType(targetConstraint); if (inference) { if (!inference.isFixed) { - const map = createObjectType(ObjectFlags.Mapped); - map.templateType = source; - map.constraintType = (target).indexType; - map.typeParameter = createType(TypeFlags.TypeParameter); - // TODO (weswigham): Ensure the name chosen for the unused "K" does not shadow any other type variables in the given scope, so as to not have a chance of breaking declaration emit - map.typeParameter.symbol = createSymbol(SymbolFlags.TypeParameter, "K" as __String); - map.typeParameter.constraint = map.constraintType; - map.modifiersType = (target).indexType; - map.hasQuestionToken = false; - map.hasReadonlyToken = false; - map.hasPossiblyHomomorphicConstraint = false; - (inference.indexes || (inference.indexes = [])).push(map); + // Instantiates instance of `type PartialInference = ({[K in Keys]: {[K1 in K]: T}})[Keys];` + // Where `T` is `source` and `Keys` is `target.indexType` + const inferenceTypeSymbol = getGlobalSymbol("PartialInference" as __String, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0); + const inferenceType = getDeclaredTypeOfSymbol(inferenceTypeSymbol); + if (inferenceType !== unknownType) { + const mapper = createTypeMapper(getSymbolLinks(inferenceTypeSymbol).typeParameters, [source, (target as IndexedAccessType).indexType]); + (inference.indexes || (inference.indexes = [])).push(instantiateType(inferenceType, mapper)); + } } return; } @@ -20112,28 +20111,6 @@ namespace ts { forEach(node.types, checkSourceElement); } - function isReadonlyMappedType(type: MappedType) { - if (type.hasReadonlyToken === undefined) { - type.hasReadonlyToken = !!type.declaration.readonlyToken; - } - return type.hasReadonlyToken; - } - - function isOptionalMappedType(type: MappedType) { - if (type.hasQuestionToken === undefined) { - type.hasQuestionToken = !!type.declaration.questionToken; - } - return type.hasQuestionToken; - } - - function isPossiblyHomomorphicMappedType(type: MappedType) { - if (type.hasPossiblyHomomorphicConstraint === undefined) { - const constraint = type.declaration.typeParameter.constraint; - type.hasPossiblyHomomorphicConstraint = isTypeOperatorNode(constraint) && constraint.operator === SyntaxKind.KeyOfKeyword; - } - return type.hasPossiblyHomomorphicConstraint; - } - function checkIndexedAccessIndexType(type: Type, accessNode: ElementAccessExpression | IndexedAccessTypeNode) { if (!(type.flags & TypeFlags.IndexedAccess)) { return type; @@ -20143,7 +20120,7 @@ namespace ts { const indexType = (type).indexType; if (isTypeAssignableTo(indexType, getIndexType(objectType))) { if (accessNode.kind === SyntaxKind.ElementAccessExpression && isAssignmentTarget(accessNode) && - getObjectFlags(objectType) & ObjectFlags.Mapped && isReadonlyMappedType(objectType)) { + getObjectFlags(objectType) & ObjectFlags.Mapped && (objectType).declaration.readonlyToken) { error(accessNode, Diagnostics.Index_signature_in_type_0_only_permits_reading, typeToString(objectType)); } return type; diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 0f1a68de4c20d..88eeba043ebf2 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1336,6 +1336,15 @@ type Record = { */ interface ThisType { } +/** + * Type instantiated to perform partial inferences from indexed accesses + */ +type PartialInference = ({ + [K in Keys]: { + [K1 in K]: T + } +})[Keys]; + /** * Represents a raw buffer of binary data, which is used to store data for the * different typed arrays. ArrayBuffers cannot be read from or written to directly, diff --git a/tests/baselines/reference/inferingFromAny.types b/tests/baselines/reference/inferingFromAny.types index dc660cdebc17d..70fa53cf218ea 100644 --- a/tests/baselines/reference/inferingFromAny.types +++ b/tests/baselines/reference/inferingFromAny.types @@ -293,7 +293,7 @@ var a = f18(a); var a = f19(a, a); >a : any ->f19(a, a) : { [K in K]: any; } +>f19(a, a) : { [K in Keys]: { [K1 in K]: any; }; }[K] >f19 : (k: K, x: T[K]) => T >a : any >a : any From ba064acf28177ec1fe61d1b4cedd7a1f1eabb972 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 21 Nov 2017 17:57:09 -0800 Subject: [PATCH 03/18] Add many more tests showing current behaviors --- .../reference/indexAccessCombinedInference.js | 135 +++++- .../indexAccessCombinedInference.symbols | 368 ++++++++++++++- .../indexAccessCombinedInference.types | 445 +++++++++++++++++- .../compiler/indexAccessCombinedInference.ts | 101 +++- 4 files changed, 1008 insertions(+), 41 deletions(-) diff --git a/tests/baselines/reference/indexAccessCombinedInference.js b/tests/baselines/reference/indexAccessCombinedInference.js index a3f19fa7fcc67..b31a76ef75725 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.js +++ b/tests/baselines/reference/indexAccessCombinedInference.js @@ -1,29 +1,148 @@ //// [indexAccessCombinedInference.ts] +// Simple case interface Args { TA: object, TY: object } -function foo( +declare function foo( a: T["TA"], - b: T["TY"]): T["TA"] & T["TY"] { - return undefined!; -} + b: T["TY"]): T["TA"] & T["TY"]; const x = foo({ x: { j: 12, i: 11 } -}, { y: 42 }); +}, { y: 42 }); + +// Union result type +interface A { + foo: number; +} +interface B { + bar: string; +} +declare const something: A | B; + +const y = foo(something, { bat: 42 }); + +// Union key type +interface Args2 { + TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list + TB?: object, + TY: object +} +declare function foo2( + a: T["TA"] | T["TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +declare function foo3( // Morally equivalent to foo2 + a: T["TA" | "TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +let z = foo2({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +let zz = foo3({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +z = zz; +zz = z; + +// Higher-order +interface Args3 { + Key: "A" | "B", + A: object, + B: object, + Merge: object, +} +declare const either: "A" | "B"; +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; + +const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); +const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); +const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); + +const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); +const opt4 = pickDelayed("A"); +const opt5 = pickDelayed("B"); +const opt6 = pickDelayed(either); + +// Reopenable +interface Args3 { + /** + * One must make patched parameters optional, otherwise signatures expecting the unpatched + * interface (ie, pickOne above) will not be able to produce a type satisfying the interface + * (as there are no inference sites for the new members) and will fall back to the constraints on each member + */ + Extra?: object, +} +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; +const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); + +// Interactions with `this` types +interface TPicker { + Key: keyof this, + X: number, + Y: string +} +declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; +const cx = chooseLiteral("X", 1, "no"); +const cy = chooseLiteral("Y", 0, "yes"); +const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); +const cneither = chooseLiteral("Key", 0, "no"); + +// Multiple inference sites +interface Args4 { + 0: object, + 1: Record, +} +declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; + +const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); + //// [indexAccessCombinedInference.js] -function foo(a, b) { - return undefined; -} var x = foo({ x: { j: 12, i: 11 } }, { y: 42 }); +var y = foo(something, { bat: 42 }); +var z = foo2({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +var zz = foo3({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +z = zz; +zz = z; +var opt1 = pickOne("A", { x: 12 }, { y: "" }, { z: /./ }); +var opt2 = pickOne("B", { x: 12 }, { y: "" }, { z: /./ }); +var opt3 = pickOne(either, { x: 12 }, { y: "" }, { z: /./ }); +var pickDelayed = function (x) { return pickOne(x, { j: x }, { i: x }, { chosen: x }); }; +var opt4 = pickDelayed("A"); +var opt5 = pickDelayed("B"); +var opt6 = pickDelayed(either); +var opt7 = pickOne("A", { x: 12 }, { y: "" }, { z: /./ }, { z: /./ }); +var opt8 = pickOne("B", { x: 12 }, { y: "" }, { z: /./ }, { z: /./ }); +var opt9 = pickOne(either, { x: 12 }, { y: "" }, { z: /./ }, { z: /./ }); +var cx = chooseLiteral("X", 1, "no"); +var cy = chooseLiteral("Y", 0, "yes"); +var ceither = chooseLiteral("X", 1, "yes"); +var cneither = chooseLiteral("Key", 0, "no"); +var result = dualInputs({ x: 0 }, { x: 1 }, { x: function () { return ""; } }); diff --git a/tests/baselines/reference/indexAccessCombinedInference.symbols b/tests/baselines/reference/indexAccessCombinedInference.symbols index c422efe422c8c..d108b7d1e9c72 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.symbols +++ b/tests/baselines/reference/indexAccessCombinedInference.symbols @@ -1,46 +1,372 @@ === tests/cases/compiler/indexAccessCombinedInference.ts === +// Simple case interface Args { >Args : Symbol(Args, Decl(indexAccessCombinedInference.ts, 0, 0)) TA: object, ->TA : Symbol(Args.TA, Decl(indexAccessCombinedInference.ts, 0, 16)) +>TA : Symbol(Args.TA, Decl(indexAccessCombinedInference.ts, 1, 16)) TY: object ->TY : Symbol(Args.TY, Decl(indexAccessCombinedInference.ts, 1, 15)) +>TY : Symbol(Args.TY, Decl(indexAccessCombinedInference.ts, 2, 15)) } -function foo( ->foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 3, 1)) ->T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) +declare function foo( +>foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 4, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 6, 21)) >Args : Symbol(Args, Decl(indexAccessCombinedInference.ts, 0, 0)) a: T["TA"], ->a : Symbol(a, Decl(indexAccessCombinedInference.ts, 5, 29)) ->T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 6, 37)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 6, 21)) - b: T["TY"]): T["TA"] & T["TY"] { ->b : Symbol(b, Decl(indexAccessCombinedInference.ts, 6, 15)) ->T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) ->T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) ->T : Symbol(T, Decl(indexAccessCombinedInference.ts, 5, 13)) + b: T["TY"]): T["TA"] & T["TY"]; +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 7, 15)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 6, 21)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 6, 21)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 6, 21)) - return undefined!; ->undefined : Symbol(undefined) +const x = foo({ +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 10, 5)) +>foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 4, 1)) + + x: { +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 10, 15)) + + j: 12, +>j : Symbol(j, Decl(indexAccessCombinedInference.ts, 11, 8)) + + i: 11 +>i : Symbol(i, Decl(indexAccessCombinedInference.ts, 12, 14)) + } +}, { y: 42 }); +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 15, 4)) + +// Union result type +interface A { +>A : Symbol(A, Decl(indexAccessCombinedInference.ts, 15, 14)) + + foo: number; +>foo : Symbol(A.foo, Decl(indexAccessCombinedInference.ts, 18, 13)) } +interface B { +>B : Symbol(B, Decl(indexAccessCombinedInference.ts, 20, 1)) -const x = foo({ ->x : Symbol(x, Decl(indexAccessCombinedInference.ts, 11, 5)) ->foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 3, 1)) + bar: string; +>bar : Symbol(B.bar, Decl(indexAccessCombinedInference.ts, 21, 13)) +} +declare const something: A | B; +>something : Symbol(something, Decl(indexAccessCombinedInference.ts, 24, 13)) +>A : Symbol(A, Decl(indexAccessCombinedInference.ts, 15, 14)) +>B : Symbol(B, Decl(indexAccessCombinedInference.ts, 20, 1)) + +const y = foo(something, { bat: 42 }); +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 26, 5)) +>foo : Symbol(foo, Decl(indexAccessCombinedInference.ts, 4, 1)) +>something : Symbol(something, Decl(indexAccessCombinedInference.ts, 24, 13)) +>bat : Symbol(bat, Decl(indexAccessCombinedInference.ts, 26, 26)) + +// Union key type +interface Args2 { +>Args2 : Symbol(Args2, Decl(indexAccessCombinedInference.ts, 26, 38)) + + TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list +>TA : Symbol(Args2.TA, Decl(indexAccessCombinedInference.ts, 29, 17)) + + TB?: object, +>TB : Symbol(Args2.TB, Decl(indexAccessCombinedInference.ts, 30, 16)) + + TY: object +>TY : Symbol(Args2.TY, Decl(indexAccessCombinedInference.ts, 31, 16)) +} +declare function foo2( +>foo2 : Symbol(foo2, Decl(indexAccessCombinedInference.ts, 33, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) +>Args2 : Symbol(Args2, Decl(indexAccessCombinedInference.ts, 26, 38)) + + a: T["TA"] | T["TB"], +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 34, 39)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) + + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 35, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 36, 18)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 36, 29)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 34, 22)) + +declare function foo3( // Morally equivalent to foo2 +>foo3 : Symbol(foo3, Decl(indexAccessCombinedInference.ts, 36, 52)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) +>Args2 : Symbol(Args2, Decl(indexAccessCombinedInference.ts, 26, 38)) + + a: T["TA" | "TB"], +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 37, 39)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) + + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 38, 22)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) +>a : Symbol(a, Decl(indexAccessCombinedInference.ts, 39, 18)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) +>b : Symbol(b, Decl(indexAccessCombinedInference.ts, 39, 29)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 37, 22)) + +let z = foo2({ +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 40, 3)) +>foo2 : Symbol(foo2, Decl(indexAccessCombinedInference.ts, 33, 1)) + + x: { +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 40, 14)) + + j: 12, +>j : Symbol(j, Decl(indexAccessCombinedInference.ts, 41, 8)) + + i: 11 +>i : Symbol(i, Decl(indexAccessCombinedInference.ts, 42, 14)) + } +}, { y: 42 }); +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 45, 4)) + +let zz = foo3({ +>zz : Symbol(zz, Decl(indexAccessCombinedInference.ts, 46, 3)) +>foo3 : Symbol(foo3, Decl(indexAccessCombinedInference.ts, 36, 52)) x: { ->x : Symbol(x, Decl(indexAccessCombinedInference.ts, 11, 15)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 46, 15)) j: 12, ->j : Symbol(j, Decl(indexAccessCombinedInference.ts, 12, 8)) +>j : Symbol(j, Decl(indexAccessCombinedInference.ts, 47, 8)) i: 11 ->i : Symbol(i, Decl(indexAccessCombinedInference.ts, 13, 14)) +>i : Symbol(i, Decl(indexAccessCombinedInference.ts, 48, 14)) } }, { y: 42 }); ->y : Symbol(y, Decl(indexAccessCombinedInference.ts, 16, 4)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 51, 4)) + +z = zz; +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 40, 3)) +>zz : Symbol(zz, Decl(indexAccessCombinedInference.ts, 46, 3)) + +zz = z; +>zz : Symbol(zz, Decl(indexAccessCombinedInference.ts, 46, 3)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 40, 3)) + +// Higher-order +interface Args3 { +>Args3 : Symbol(Args3, Decl(indexAccessCombinedInference.ts, 53, 7), Decl(indexAccessCombinedInference.ts, 72, 33)) + + Key: "A" | "B", +>Key : Symbol(Args3.Key, Decl(indexAccessCombinedInference.ts, 56, 17)) + + A: object, +>A : Symbol(Args3.A, Decl(indexAccessCombinedInference.ts, 57, 19)) + + B: object, +>B : Symbol(Args3.B, Decl(indexAccessCombinedInference.ts, 58, 14)) + + Merge: object, +>Merge : Symbol(Args3.Merge, Decl(indexAccessCombinedInference.ts, 59, 14)) +} +declare const either: "A" | "B"; +>either : Symbol(either, Decl(indexAccessCombinedInference.ts, 62, 13)) + +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>Args3 : Symbol(Args3, Decl(indexAccessCombinedInference.ts, 53, 7), Decl(indexAccessCombinedInference.ts, 72, 33)) +>key : Symbol(key, Decl(indexAccessCombinedInference.ts, 63, 42)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>left : Symbol(left, Decl(indexAccessCombinedInference.ts, 63, 56)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>right : Symbol(right, Decl(indexAccessCombinedInference.ts, 63, 70)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>into : Symbol(into, Decl(indexAccessCombinedInference.ts, 63, 85)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 63, 25)) + +const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); +>opt1 : Symbol(opt1, Decl(indexAccessCombinedInference.ts, 65, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 65, 27)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 65, 36)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 65, 45)) + +const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); +>opt2 : Symbol(opt2, Decl(indexAccessCombinedInference.ts, 66, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 66, 27)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 66, 36)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 66, 45)) + +const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); +>opt3 : Symbol(opt3, Decl(indexAccessCombinedInference.ts, 67, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>either : Symbol(either, Decl(indexAccessCombinedInference.ts, 62, 13)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 67, 30)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 67, 39)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 67, 48)) + +const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); +>pickDelayed : Symbol(pickDelayed, Decl(indexAccessCombinedInference.ts, 69, 5)) +>TKey : Symbol(TKey, Decl(indexAccessCombinedInference.ts, 69, 21)) +>Args3 : Symbol(Args3, Decl(indexAccessCombinedInference.ts, 53, 7), Decl(indexAccessCombinedInference.ts, 72, 33)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 69, 48)) +>TKey : Symbol(TKey, Decl(indexAccessCombinedInference.ts, 69, 21)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 69, 48)) +>j : Symbol(j, Decl(indexAccessCombinedInference.ts, 69, 72)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 69, 48)) +>i : Symbol(i, Decl(indexAccessCombinedInference.ts, 69, 80)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 69, 48)) +>chosen : Symbol(chosen, Decl(indexAccessCombinedInference.ts, 69, 88)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 69, 48)) + +const opt4 = pickDelayed("A"); +>opt4 : Symbol(opt4, Decl(indexAccessCombinedInference.ts, 70, 5)) +>pickDelayed : Symbol(pickDelayed, Decl(indexAccessCombinedInference.ts, 69, 5)) + +const opt5 = pickDelayed("B"); +>opt5 : Symbol(opt5, Decl(indexAccessCombinedInference.ts, 71, 5)) +>pickDelayed : Symbol(pickDelayed, Decl(indexAccessCombinedInference.ts, 69, 5)) + +const opt6 = pickDelayed(either); +>opt6 : Symbol(opt6, Decl(indexAccessCombinedInference.ts, 72, 5)) +>pickDelayed : Symbol(pickDelayed, Decl(indexAccessCombinedInference.ts, 69, 5)) +>either : Symbol(either, Decl(indexAccessCombinedInference.ts, 62, 13)) + +// Reopenable +interface Args3 { +>Args3 : Symbol(Args3, Decl(indexAccessCombinedInference.ts, 53, 7), Decl(indexAccessCombinedInference.ts, 72, 33)) + + /** + * One must make patched parameters optional, otherwise signatures expecting the unpatched + * interface (ie, pickOne above) will not be able to produce a type satisfying the interface + * (as there are no inference sites for the new members) and will fall back to the constraints on each member + */ + Extra?: object, +>Extra : Symbol(Args3.Extra, Decl(indexAccessCombinedInference.ts, 75, 17)) +} +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>Args3 : Symbol(Args3, Decl(indexAccessCombinedInference.ts, 53, 7), Decl(indexAccessCombinedInference.ts, 72, 33)) +>key : Symbol(key, Decl(indexAccessCombinedInference.ts, 83, 42)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>left : Symbol(left, Decl(indexAccessCombinedInference.ts, 83, 56)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>right : Symbol(right, Decl(indexAccessCombinedInference.ts, 83, 70)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>into : Symbol(into, Decl(indexAccessCombinedInference.ts, 83, 85)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>extra : Symbol(extra, Decl(indexAccessCombinedInference.ts, 83, 103)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>into : Symbol(into, Decl(indexAccessCombinedInference.ts, 83, 139)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) +>extra : Symbol(extra, Decl(indexAccessCombinedInference.ts, 83, 156)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 83, 25)) + +const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt7 : Symbol(opt7, Decl(indexAccessCombinedInference.ts, 84, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 84, 27)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 84, 36)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 84, 45)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 84, 55)) + +const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt8 : Symbol(opt8, Decl(indexAccessCombinedInference.ts, 85, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 85, 27)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 85, 36)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 85, 45)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 85, 55)) + +const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt9 : Symbol(opt9, Decl(indexAccessCombinedInference.ts, 86, 5)) +>pickOne : Symbol(pickOne, Decl(indexAccessCombinedInference.ts, 62, 32), Decl(indexAccessCombinedInference.ts, 82, 1)) +>either : Symbol(either, Decl(indexAccessCombinedInference.ts, 62, 13)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 86, 30)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 86, 39)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 86, 48)) +>z : Symbol(z, Decl(indexAccessCombinedInference.ts, 86, 58)) + +// Interactions with `this` types +interface TPicker { +>TPicker : Symbol(TPicker, Decl(indexAccessCombinedInference.ts, 86, 67)) + + Key: keyof this, +>Key : Symbol(TPicker.Key, Decl(indexAccessCombinedInference.ts, 89, 19)) + + X: number, +>X : Symbol(TPicker.X, Decl(indexAccessCombinedInference.ts, 90, 20)) + + Y: string +>Y : Symbol(TPicker.Y, Decl(indexAccessCombinedInference.ts, 91, 14)) +} +declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; +>chooseLiteral : Symbol(chooseLiteral, Decl(indexAccessCombinedInference.ts, 93, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) +>TPicker : Symbol(TPicker, Decl(indexAccessCombinedInference.ts, 86, 67)) +>choice : Symbol(choice, Decl(indexAccessCombinedInference.ts, 94, 50)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 94, 67)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 94, 78)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 94, 31)) + +const cx = chooseLiteral("X", 1, "no"); +>cx : Symbol(cx, Decl(indexAccessCombinedInference.ts, 95, 5)) +>chooseLiteral : Symbol(chooseLiteral, Decl(indexAccessCombinedInference.ts, 93, 1)) + +const cy = chooseLiteral("Y", 0, "yes"); +>cy : Symbol(cy, Decl(indexAccessCombinedInference.ts, 96, 5)) +>chooseLiteral : Symbol(chooseLiteral, Decl(indexAccessCombinedInference.ts, 93, 1)) + +const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); +>ceither : Symbol(ceither, Decl(indexAccessCombinedInference.ts, 97, 5)) +>chooseLiteral : Symbol(chooseLiteral, Decl(indexAccessCombinedInference.ts, 93, 1)) + +const cneither = chooseLiteral("Key", 0, "no"); +>cneither : Symbol(cneither, Decl(indexAccessCombinedInference.ts, 98, 5)) +>chooseLiteral : Symbol(chooseLiteral, Decl(indexAccessCombinedInference.ts, 93, 1)) + +// Multiple inference sites +interface Args4 { +>Args4 : Symbol(Args4, Decl(indexAccessCombinedInference.ts, 98, 47)) + + 0: object, + 1: Record, +>Record : Symbol(Record, Decl(lib.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +} +declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; +>dualInputs : Symbol(dualInputs, Decl(indexAccessCombinedInference.ts, 104, 1)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) +>Args4 : Symbol(Args4, Decl(indexAccessCombinedInference.ts, 98, 47)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 105, 45)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 105, 55)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) +>toDelay : Symbol(toDelay, Decl(indexAccessCombinedInference.ts, 105, 66)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) +>transformers : Symbol(transformers, Decl(indexAccessCombinedInference.ts, 105, 95)) +>T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) + +const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); +>result : Symbol(result, Decl(indexAccessCombinedInference.ts, 107, 5)) +>dualInputs : Symbol(dualInputs, Decl(indexAccessCombinedInference.ts, 104, 1)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 107, 27)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 107, 35)) +>x : Symbol(x, Decl(indexAccessCombinedInference.ts, 107, 43)) diff --git a/tests/baselines/reference/indexAccessCombinedInference.types b/tests/baselines/reference/indexAccessCombinedInference.types index f32c28de6266f..03badaade7613 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.types +++ b/tests/baselines/reference/indexAccessCombinedInference.types @@ -1,4 +1,5 @@ === tests/cases/compiler/indexAccessCombinedInference.ts === +// Simple case interface Args { >Args : Args @@ -9,7 +10,7 @@ interface Args { >TY : object } -function foo( +declare function foo( >foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] >T : T >Args : Args @@ -18,17 +19,12 @@ function foo( >a : T["TA"] >T : T - b: T["TY"]): T["TA"] & T["TY"] { + b: T["TY"]): T["TA"] & T["TY"]; >b : T["TY"] >T : T >T : T >T : T - return undefined!; ->undefined! : undefined ->undefined : undefined -} - const x = foo({ >x : { x: { j: number; i: number; }; } & { y: number; } >foo({ x: { j: 12, i: 11 }}, { y: 42 }) : { x: { j: number; i: number; }; } & { y: number; } @@ -52,3 +48,438 @@ const x = foo({ >y : number >42 : 42 +// Union result type +interface A { +>A : A + + foo: number; +>foo : number +} +interface B { +>B : B + + bar: string; +>bar : string +} +declare const something: A | B; +>something : A | B +>A : A +>B : B + +const y = foo(something, { bat: 42 }); +>y : (A & { bat: number; }) | (B & { bat: number; }) +>foo(something, { bat: 42 }) : (A & { bat: number; }) | (B & { bat: number; }) +>foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] +>something : A | B +>{ bat: 42 } : { bat: number; } +>bat : number +>42 : 42 + +// Union key type +interface Args2 { +>Args2 : Args2 + + TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list +>TA : object + + TB?: object, +>TB : object + + TY: object +>TY : object +} +declare function foo2( +>foo2 : (a: T["TA"] | T["TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] +>T : T +>Args2 : Args2 + + a: T["TA"] | T["TB"], +>a : T["TA"] | T["TB"] +>T : T +>T : T + + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +>b : T["TY"] +>T : T +>a : T["TA"] +>T : T +>b : T["TB"] +>T : T +>T : T + +declare function foo3( // Morally equivalent to foo2 +>foo3 : (a: T["TA" | "TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] +>T : T +>Args2 : Args2 + + a: T["TA" | "TB"], +>a : T["TA" | "TB"] +>T : T + + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +>b : T["TY"] +>T : T +>a : T["TA"] +>T : T +>b : T["TB"] +>T : T +>T : T + +let z = foo2({ +>z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } +>foo2({ x: { j: 12, i: 11 }}, { y: 42 }) : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } +>foo2 : (a: T["TA"] | T["TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] +>{ x: { j: 12, i: 11 }} : { x: { j: number; i: number; }; } + + x: { +>x : { j: number; i: number; } +>{ j: 12, i: 11 } : { j: number; i: number; } + + j: 12, +>j : number +>12 : 12 + + i: 11 +>i : number +>11 : 11 + } +}, { y: 42 }); +>{ y: 42 } : { y: number; } +>y : number +>42 : 42 + +let zz = foo3({ +>zz : { a: any; b: any; } & { y: number; } +>foo3({ x: { j: 12, i: 11 }}, { y: 42 }) : { a: any; b: any; } & { y: number; } +>foo3 : (a: T["TA" | "TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] +>{ x: { j: 12, i: 11 }} : { x: { j: number; i: number; }; } + + x: { +>x : { j: number; i: number; } +>{ j: 12, i: 11 } : { j: number; i: number; } + + j: 12, +>j : number +>12 : 12 + + i: 11 +>i : number +>11 : 11 + } +}, { y: 42 }); +>{ y: 42 } : { y: number; } +>y : number +>42 : 42 + +z = zz; +>z = zz : { a: any; b: any; } & { y: number; } +>z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } +>zz : { a: any; b: any; } & { y: number; } + +zz = z; +>zz = z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } +>zz : { a: any; b: any; } & { y: number; } +>z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } + +// Higher-order +interface Args3 { +>Args3 : Args3 + + Key: "A" | "B", +>Key : "A" | "B" + + A: object, +>A : object + + B: object, +>B : object + + Merge: object, +>Merge : object +} +declare const either: "A" | "B"; +>either : "A" | "B" + +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>T : T +>Args3 : Args3 +>key : T["Key"] +>T : T +>left : T["A"] +>T : T +>right : T["B"] +>T : T +>into : T["Merge"] +>T : T +>T : T +>T : T +>T : T + +const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); +>opt1 : { x: number; } & { z: RegExp; } +>pickOne("A", {x: 12}, {y: ""}, {z: /./}) : { x: number; } & { z: RegExp; } +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>"A" : "A" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); +>opt2 : { y: string; } & { z: RegExp; } +>pickOne("B", {x: 12}, {y: ""}, {z: /./}) : { y: string; } & { z: RegExp; } +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>"B" : "B" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); +>opt3 : ({ x: number; } & { z: RegExp; }) | ({ y: string; } & { z: RegExp; }) +>pickOne(either, {x: 12}, {y: ""}, {z: /./}) : ({ x: number; } & { z: RegExp; }) | ({ y: string; } & { z: RegExp; }) +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>either : "A" | "B" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); +>pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>(x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}) : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>TKey : TKey +>Args3 : Args3 +>x : TKey +>TKey : TKey +>pickOne(x, {j: x}, {i: x}, {chosen: x}) : ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>x : TKey +>{j: x} : { j: TKey; } +>j : TKey +>x : TKey +>{i: x} : { i: TKey; } +>i : TKey +>x : TKey +>{chosen: x} : { chosen: TKey; } +>chosen : TKey +>x : TKey + +const opt4 = pickDelayed("A"); +>opt4 : { j: "A"; } & { chosen: "A"; } +>pickDelayed("A") : { j: "A"; } & { chosen: "A"; } +>pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>"A" : "A" + +const opt5 = pickDelayed("B"); +>opt5 : { i: "B"; } & { chosen: "B"; } +>pickDelayed("B") : { i: "B"; } & { chosen: "B"; } +>pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>"B" : "B" + +const opt6 = pickDelayed(either); +>opt6 : ({ j: "A" | "B"; } & { chosen: "A" | "B"; }) | ({ i: "A" | "B"; } & { chosen: "A" | "B"; }) +>pickDelayed(either) : ({ j: "A" | "B"; } & { chosen: "A" | "B"; }) | ({ i: "A" | "B"; } & { chosen: "A" | "B"; }) +>pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } +>either : "A" | "B" + +// Reopenable +interface Args3 { +>Args3 : Args3 + + /** + * One must make patched parameters optional, otherwise signatures expecting the unpatched + * interface (ie, pickOne above) will not be able to produce a type satisfying the interface + * (as there are no inference sites for the new members) and will fall back to the constraints on each member + */ + Extra?: object, +>Extra : object +} +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>T : T +>Args3 : Args3 +>key : T["Key"] +>T : T +>left : T["A"] +>T : T +>right : T["B"] +>T : T +>into : T["Merge"] +>T : T +>extra : T["Extra"] +>T : T +>T : T +>T : T +>into : T["Merge"] +>T : T +>extra : T["Extra"] +>T : T + +const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt7 : { x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; } +>pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}) : { x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; } +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>"A" : "A" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt8 : { y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; } +>pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}) : { y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; } +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>"B" : "B" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); +>opt9 : ({ x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) | ({ y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) +>pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}) : ({ x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) | ({ y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } +>either : "A" | "B" +>{x: 12} : { x: number; } +>x : number +>12 : 12 +>{y: ""} : { y: string; } +>y : string +>"" : "" +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp +>{z: /./} : { z: RegExp; } +>z : RegExp +>/./ : RegExp + +// Interactions with `this` types +interface TPicker { +>TPicker : TPicker + + Key: keyof this, +>Key : keyof this + + X: number, +>X : number + + Y: string +>Y : string +} +declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; +>chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] +>T : T +>TPicker : TPicker +>choice : T["Key"] +>T : T +>x : T["X"] +>T : T +>y : T["Y"] +>T : T +>T : T +>T : T + +const cx = chooseLiteral("X", 1, "no"); +>cx : 1 +>chooseLiteral("X", 1, "no") : 1 +>chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] +>"X" : "X" +>1 : 1 +>"no" : "no" + +const cy = chooseLiteral("Y", 0, "yes"); +>cy : "yes" +>chooseLiteral("Y", 0, "yes") : "yes" +>chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] +>"Y" : "Y" +>0 : 0 +>"yes" : "yes" + +const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); +>ceither : 1 | "yes" +>chooseLiteral("X" as "X" | "Y", 1, "yes") : 1 | "yes" +>chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] +>"X" as "X" | "Y" : "X" | "Y" +>"X" : "X" +>1 : 1 +>"yes" : "yes" + +const cneither = chooseLiteral("Key", 0, "no"); +>cneither : "Key" +>chooseLiteral("Key", 0, "no") : "Key" +>chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] +>"Key" : "Key" +>0 : 0 +>"no" : "no" + +// Multiple inference sites +interface Args4 { +>Args4 : Args4 + + 0: object, + 1: Record, +>Record : Record +>Function : Function +} +declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; +>dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"]; } +>T : T +>Args4 : Args4 +>x : T["0"] +>T : T +>y : T["0"] +>T : T +>toDelay : T["1"] +>T : T +>T : T +>transformers : T["1"] +>T : T + +const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); +>result : { x: number; } & { x: number; } & { transformers: { x: () => ""; }; } +>dualInputs({x: 0}, {x: 1}, {x: () => ""}) : { x: number; } & { x: number; } & { transformers: { x: () => ""; }; } +>dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"]; } +>{x: 0} : { x: number; } +>x : number +>0 : 0 +>{x: 1} : { x: number; } +>x : number +>1 : 1 +>{x: () => ""} : { x: () => ""; } +>x : () => "" +>() => "" : () => "" +>"" : "" + diff --git a/tests/cases/compiler/indexAccessCombinedInference.ts b/tests/cases/compiler/indexAccessCombinedInference.ts index c1d7f03ac22ee..59af2dd60fa1f 100644 --- a/tests/cases/compiler/indexAccessCombinedInference.ts +++ b/tests/cases/compiler/indexAccessCombinedInference.ts @@ -1,17 +1,108 @@ +// Simple case interface Args { TA: object, TY: object } -function foo( +declare function foo( a: T["TA"], - b: T["TY"]): T["TA"] & T["TY"] { - return undefined!; -} + b: T["TY"]): T["TA"] & T["TY"]; const x = foo({ x: { j: 12, i: 11 } -}, { y: 42 }); \ No newline at end of file +}, { y: 42 }); + +// Union result type +interface A { + foo: number; +} +interface B { + bar: string; +} +declare const something: A | B; + +const y = foo(something, { bat: 42 }); + +// Union key type +interface Args2 { + TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list + TB?: object, + TY: object +} +declare function foo2( + a: T["TA"] | T["TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +declare function foo3( // Morally equivalent to foo2 + a: T["TA" | "TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; +let z = foo2({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +let zz = foo3({ + x: { + j: 12, + i: 11 + } +}, { y: 42 }); +z = zz; +zz = z; + +// Higher-order +interface Args3 { + Key: "A" | "B", + A: object, + B: object, + Merge: object, +} +declare const either: "A" | "B"; +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; + +const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); +const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); +const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); + +const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); +const opt4 = pickDelayed("A"); +const opt5 = pickDelayed("B"); +const opt6 = pickDelayed(either); + +// Reopenable +interface Args3 { + /** + * One must make patched parameters optional, otherwise signatures expecting the unpatched + * interface (ie, pickOne above) will not be able to produce a type satisfying the interface + * (as there are no inference sites for the new members) and will fall back to the constraints on each member + */ + Extra?: object, +} +declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; +const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); +const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); + +// Interactions with `this` types +interface TPicker { + Key: keyof this, + X: number, + Y: string +} +declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; +const cx = chooseLiteral("X", 1, "no"); +const cy = chooseLiteral("Y", 0, "yes"); +const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); +const cneither = chooseLiteral("Key", 0, "no"); + +// Multiple inference sites +interface Args4 { + 0: object, + 1: Record, +} +declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; + +const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); From 93afc105479da2b36798b4f6a0a8f6cb87778512 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 1 Dec 2017 16:44:45 -0500 Subject: [PATCH 04/18] Discriminate partial inferences if not complete enough to satisfy constraint --- src/compiler/checker.ts | 33 +++++++++++- .../reference/typeInferenceOnIndexUnion.js | 13 +++++ .../typeInferenceOnIndexUnion.symbols | 45 ++++++++++++++++ .../reference/typeInferenceOnIndexUnion.types | 52 +++++++++++++++++++ .../compiler/typeInferenceOnIndexUnion.ts | 7 +++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typeInferenceOnIndexUnion.js create mode 100644 tests/baselines/reference/typeInferenceOnIndexUnion.symbols create mode 100644 tests/baselines/reference/typeInferenceOnIndexUnion.types create mode 100644 tests/cases/compiler/typeInferenceOnIndexUnion.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d5e27dbeba82d..d9d96012fbc5a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11686,7 +11686,38 @@ namespace ts { if (!inferredType) { if (inference.indexes) { // Build a candidate from all indexes - (inference.candidates || (inference.candidates = [])).push(getIntersectionType(inference.indexes)); + let aggregateInference = getIntersectionType(inference.indexes); + const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]); + if (constraint) { + const instantiatedConstraint = instantiateType(constraint, context); + if (!context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { + if (instantiatedConstraint.flags & TypeFlags.Union) { + const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); + if (discriminantProps) { + let match: Type; + findDiscriminant: for (const p of discriminantProps) { + const candidatePropType = getTypeOfPropertyOfType(aggregateInference, p.escapedName); + for (const type of (instantiatedConstraint as UnionType).types) { + const propType = getTypeOfPropertyOfType(type, p.escapedName); + if (propType && checkTypeAssignableTo(candidatePropType, propType, /*errorNode*/ undefined)) { + if (match && match !== type) { + match = undefined; + break findDiscriminant; + } + else { + match = type; + } + } + } + } + if (match) { + aggregateInference = getSpreadType(match, aggregateInference, /*symbol*/ undefined, /*propegatedFlags*/ 0); + } + } + } + } + } + (inference.candidates || (inference.candidates = [])).push(aggregateInference); } if (inference.candidates) { // Extract all object literal types and replace them with a single widened and normalized type. diff --git a/tests/baselines/reference/typeInferenceOnIndexUnion.js b/tests/baselines/reference/typeInferenceOnIndexUnion.js new file mode 100644 index 0000000000000..fc32ef0653e85 --- /dev/null +++ b/tests/baselines/reference/typeInferenceOnIndexUnion.js @@ -0,0 +1,13 @@ +//// [typeInferenceOnIndexUnion.ts] +type Options = { k: "a", a: number } | { k: "b", b: string }; +declare function f(p: T["k"]): T; +const x = f("a"); // expect it to be `{ k: "a", a: number }` + +type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; +declare function f2(p: T["k"], c: T["c"]): T; +const x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` + + +//// [typeInferenceOnIndexUnion.js] +var x = f("a"); // expect it to be `{ k: "a", a: number }` +var x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` diff --git a/tests/baselines/reference/typeInferenceOnIndexUnion.symbols b/tests/baselines/reference/typeInferenceOnIndexUnion.symbols new file mode 100644 index 0000000000000..b3a0d0dc98f91 --- /dev/null +++ b/tests/baselines/reference/typeInferenceOnIndexUnion.symbols @@ -0,0 +1,45 @@ +=== tests/cases/compiler/typeInferenceOnIndexUnion.ts === +type Options = { k: "a", a: number } | { k: "b", b: string }; +>Options : Symbol(Options, Decl(typeInferenceOnIndexUnion.ts, 0, 0)) +>k : Symbol(k, Decl(typeInferenceOnIndexUnion.ts, 0, 16)) +>a : Symbol(a, Decl(typeInferenceOnIndexUnion.ts, 0, 24)) +>k : Symbol(k, Decl(typeInferenceOnIndexUnion.ts, 0, 40)) +>b : Symbol(b, Decl(typeInferenceOnIndexUnion.ts, 0, 48)) + +declare function f(p: T["k"]): T; +>f : Symbol(f, Decl(typeInferenceOnIndexUnion.ts, 0, 61)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 1, 19)) +>Options : Symbol(Options, Decl(typeInferenceOnIndexUnion.ts, 0, 0)) +>p : Symbol(p, Decl(typeInferenceOnIndexUnion.ts, 1, 38)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 1, 19)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 1, 19)) + +const x = f("a"); // expect it to be `{ k: "a", a: number }` +>x : Symbol(x, Decl(typeInferenceOnIndexUnion.ts, 2, 5)) +>f : Symbol(f, Decl(typeInferenceOnIndexUnion.ts, 0, 61)) + +type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; +>Options2 : Symbol(Options2, Decl(typeInferenceOnIndexUnion.ts, 2, 17)) +>k : Symbol(k, Decl(typeInferenceOnIndexUnion.ts, 4, 17)) +>a : Symbol(a, Decl(typeInferenceOnIndexUnion.ts, 4, 25)) +>c : Symbol(c, Decl(typeInferenceOnIndexUnion.ts, 4, 36)) +>k : Symbol(k, Decl(typeInferenceOnIndexUnion.ts, 4, 48)) +>b : Symbol(b, Decl(typeInferenceOnIndexUnion.ts, 4, 56)) +>c : Symbol(c, Decl(typeInferenceOnIndexUnion.ts, 4, 67)) + +declare function f2(p: T["k"], c: T["c"]): T; +>f2 : Symbol(f2, Decl(typeInferenceOnIndexUnion.ts, 4, 76)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 5, 20)) +>Options2 : Symbol(Options2, Decl(typeInferenceOnIndexUnion.ts, 2, 17)) +>p : Symbol(p, Decl(typeInferenceOnIndexUnion.ts, 5, 40)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 5, 20)) +>c : Symbol(c, Decl(typeInferenceOnIndexUnion.ts, 5, 50)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 5, 20)) +>T : Symbol(T, Decl(typeInferenceOnIndexUnion.ts, 5, 20)) + +const x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` +>x2 : Symbol(x2, Decl(typeInferenceOnIndexUnion.ts, 6, 5)) +>f2 : Symbol(f2, Decl(typeInferenceOnIndexUnion.ts, 4, 76)) +>x : Symbol(x, Decl(typeInferenceOnIndexUnion.ts, 6, 20)) +>y : Symbol(y, Decl(typeInferenceOnIndexUnion.ts, 6, 26)) + diff --git a/tests/baselines/reference/typeInferenceOnIndexUnion.types b/tests/baselines/reference/typeInferenceOnIndexUnion.types new file mode 100644 index 0000000000000..57e171d86ba74 --- /dev/null +++ b/tests/baselines/reference/typeInferenceOnIndexUnion.types @@ -0,0 +1,52 @@ +=== tests/cases/compiler/typeInferenceOnIndexUnion.ts === +type Options = { k: "a", a: number } | { k: "b", b: string }; +>Options : Options +>k : "a" +>a : number +>k : "b" +>b : string + +declare function f(p: T["k"]): T; +>f : (p: T["k"]) => T +>T : T +>Options : Options +>p : T["k"] +>T : T +>T : T + +const x = f("a"); // expect it to be `{ k: "a", a: number }` +>x : { k: "a"; a: number; } +>f("a") : { k: "a"; a: number; } +>f : (p: T["k"]) => T +>"a" : "a" + +type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; +>Options2 : Options2 +>k : "a" +>a : number +>c : {} +>k : "b" +>b : string +>c : {} + +declare function f2(p: T["k"], c: T["c"]): T; +>f2 : (p: T["k"], c: T["c"]) => T +>T : T +>Options2 : Options2 +>p : T["k"] +>T : T +>c : T["c"] +>T : T +>T : T + +const x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` +>x2 : { k: "a"; c: { x: number; y: number; }; a: number; } +>f2("a", { x: 1, y: 2 }) : { k: "a"; c: { x: number; y: number; }; a: number; } +>f2 : (p: T["k"], c: T["c"]) => T +>"a" : "a" +>{ x: 1, y: 2 } : { x: number; y: number; } +>x : number +>1 : 1 +>y : number +>2 : 2 + diff --git a/tests/cases/compiler/typeInferenceOnIndexUnion.ts b/tests/cases/compiler/typeInferenceOnIndexUnion.ts new file mode 100644 index 0000000000000..35c5fb16a9fa8 --- /dev/null +++ b/tests/cases/compiler/typeInferenceOnIndexUnion.ts @@ -0,0 +1,7 @@ +type Options = { k: "a", a: number } | { k: "b", b: string }; +declare function f(p: T["k"]): T; +const x = f("a"); // expect it to be `{ k: "a", a: number }` + +type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; +declare function f2(p: T["k"], c: T["c"]): T; +const x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` From 49e19617b41ee17a536ed50870c4e315c2c4f548 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 21 Dec 2017 14:58:29 -0800 Subject: [PATCH 05/18] Move case to prefered location --- src/compiler/checker.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d9d96012fbc5a..95b943ebde094 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11401,23 +11401,6 @@ namespace ts { } return; } - else if (target.flags & TypeFlags.IndexedAccess) { - const targetConstraint = (target).objectType; - const inference = getInferenceInfoForType(targetConstraint); - if (inference) { - if (!inference.isFixed) { - // Instantiates instance of `type PartialInference = ({[K in Keys]: {[K1 in K]: T}})[Keys];` - // Where `T` is `source` and `Keys` is `target.indexType` - const inferenceTypeSymbol = getGlobalSymbol("PartialInference" as __String, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0); - const inferenceType = getDeclaredTypeOfSymbol(inferenceTypeSymbol); - if (inferenceType !== unknownType) { - const mapper = createTypeMapper(getSymbolLinks(inferenceTypeSymbol).typeParameters, [source, (target as IndexedAccessType).indexType]); - (inference.indexes || (inference.indexes = [])).push(instantiateType(inferenceType, mapper)); - } - } - return; - } - } } if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { // If source and target are references to the same generic type, infer from type arguments @@ -11449,6 +11432,23 @@ namespace ts { inferFromTypes((source).objectType, (target).objectType); inferFromTypes((source).indexType, (target).indexType); } + else if (target.flags & TypeFlags.IndexedAccess) { + const targetConstraint = (target).objectType; + const inference = getInferenceInfoForType(targetConstraint); + if (inference) { + if (!inference.isFixed) { + // Instantiates instance of `type PartialInference = ({[K in Keys]: {[K1 in K]: T}})[Keys];` + // Where `T` is `source` and `Keys` is `target.indexType` + const inferenceTypeSymbol = getGlobalSymbol("PartialInference" as __String, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0); + const inferenceType = getDeclaredTypeOfSymbol(inferenceTypeSymbol); + if (inferenceType !== unknownType) { + const mapper = createTypeMapper(getSymbolLinks(inferenceTypeSymbol).typeParameters, [source, (target as IndexedAccessType).indexType]); + (inference.indexes || (inference.indexes = [])).push(instantiateType(inferenceType, mapper)); + } + } + return; + } + } else if (target.flags & TypeFlags.UnionOrIntersection) { const targetTypes = (target).types; let typeVariableCount = 0; From 96772e5d88626ab7063acc0bdeedd1e0505abffa Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 21 Dec 2017 15:13:11 -0800 Subject: [PATCH 06/18] Small refactor to reduce how many comparisons are performed --- src/compiler/checker.ts | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 95b943ebde094..f6d05ad27b5e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11690,29 +11690,27 @@ namespace ts { const constraint = getConstraintOfTypeParameter(context.signature.typeParameters[index]); if (constraint) { const instantiatedConstraint = instantiateType(constraint, context); - if (!context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { - if (instantiatedConstraint.flags & TypeFlags.Union) { - const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); - if (discriminantProps) { - let match: Type; - findDiscriminant: for (const p of discriminantProps) { - const candidatePropType = getTypeOfPropertyOfType(aggregateInference, p.escapedName); - for (const type of (instantiatedConstraint as UnionType).types) { - const propType = getTypeOfPropertyOfType(type, p.escapedName); - if (propType && checkTypeAssignableTo(candidatePropType, propType, /*errorNode*/ undefined)) { - if (match && match !== type) { - match = undefined; - break findDiscriminant; - } - else { - match = type; - } + if (instantiatedConstraint.flags & TypeFlags.Union && !context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { + const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); + if (discriminantProps) { + let match: Type; + findDiscriminant: for (const p of discriminantProps) { + const candidatePropType = getTypeOfPropertyOfType(aggregateInference, p.escapedName); + for (const type of (instantiatedConstraint as UnionType).types) { + const propType = getTypeOfPropertyOfType(type, p.escapedName); + if (propType && checkTypeAssignableTo(candidatePropType, propType, /*errorNode*/ undefined)) { + if (match && match !== type) { + match = undefined; + break findDiscriminant; + } + else { + match = type; } } } - if (match) { - aggregateInference = getSpreadType(match, aggregateInference, /*symbol*/ undefined, /*propegatedFlags*/ 0); - } + } + if (match) { + aggregateInference = getSpreadType(match, aggregateInference, /*symbol*/ undefined, /*propegatedFlags*/ 0); } } } From 2e0c6354b729c94c157500d22389adadb14788a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 28 Feb 2023 10:49:19 +0100 Subject: [PATCH 07/18] Infer reverse mapped types based on inferences made for its concrete properties --- src/compiler/checker.ts | 28 ++- ...eMappedConcretePropertiesInference.symbols | 183 ++++++++++++++++++ ...rseMappedConcretePropertiesInference.types | 179 +++++++++++++++++ ...everseMappedConcretePropertiesInference.ts | 60 ++++++ 4 files changed, 446 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/reverseMappedConcretePropertiesInference.symbols create mode 100644 tests/baselines/reference/reverseMappedConcretePropertiesInference.types create mode 100644 tests/cases/compiler/reverseMappedConcretePropertiesInference.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70e0220bbb39c..fc8cce8d58087 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23935,9 +23935,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type { const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter; const templateType = getTemplateTypeFromMappedType(target); - const inference = createInferenceInfo(typeParameter); - inferTypes([inference], sourceType, templateType); - return getTypeFromInference(inference) || unknownType; + const inferences = [createInferenceInfo(typeParameter)]; + inferTypes(inferences, sourceType, templateType); + const wholeInferredType = getTypeFromInference(inferences[0]); + if (!wholeInferredType && inferences.length > 1) { + const members = createSymbolTable(); + for (let i = 1; i < inferences.length; i++) { + const indexType = (inferences[i].typeParameter as IndexedAccessType).indexType as StringLiteralType; + const name = escapeLeadingUnderscores(indexType.value); + const prop = createSymbol(SymbolFlags.Property, name); + prop.links.type = getTypeFromInference(inferences[i]) || unknownType; + members.set(name, prop); + } + return createAnonymousType(undefined, members, emptyArray, emptyArray, emptyArray); + } + return wholeInferredType || unknownType; } function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator { @@ -24255,7 +24267,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isFromInferenceBlockedSource(source)) { return; } - const inference = getInferenceInfoForType(target); + let inference = getInferenceInfoForType(target); + if ( + !inference && (target.flags & TypeFlags.IndexedAccess) && + inferences[0].typeParameter.flags & TypeFlags.IndexedAccess && + (target as IndexedAccessType).indexType.flags & TypeFlags.StringLiteral && + ((target as IndexedAccessType).objectType.flags & TypeFlags.IndexedAccess) + ) { + inferences.push(inference = createInferenceInfo(target)); + } if (inference) { // If target is a type parameter, make an inference, unless the source type contains // a "non-inferrable" type. Types with this flag set are markers used to prevent inference. diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.symbols b/tests/baselines/reference/reverseMappedConcretePropertiesInference.symbols new file mode 100644 index 0000000000000..658734ebae9ef --- /dev/null +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.symbols @@ -0,0 +1,183 @@ +=== tests/cases/compiler/reverseMappedConcretePropertiesInference.ts === +declare function test>(a: { +>test : Symbol(test, Decl(reverseMappedConcretePropertiesInference.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>foo : Symbol(foo, Decl(reverseMappedConcretePropertiesInference.ts, 0, 48)) +>bar : Symbol(bar, Decl(reverseMappedConcretePropertiesInference.ts, 0, 62)) +>a : Symbol(a, Decl(reverseMappedConcretePropertiesInference.ts, 0, 80)) + + [K in keyof T]: { +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 1, 3)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) + + foo: T[K]["foo"]; +>foo : Symbol(foo, Decl(reverseMappedConcretePropertiesInference.ts, 1, 19)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 1, 3)) + + onFoo: (fooArg: T[K]["foo"]) => void; +>onFoo : Symbol(onFoo, Decl(reverseMappedConcretePropertiesInference.ts, 2, 21)) +>fooArg : Symbol(fooArg, Decl(reverseMappedConcretePropertiesInference.ts, 3, 12)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 1, 3)) + + bar: T[K]["bar"]; +>bar : Symbol(bar, Decl(reverseMappedConcretePropertiesInference.ts, 3, 41)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 1, 3)) + + onBar: (barArg: T[K]["bar"]) => void; +>onBar : Symbol(onBar, Decl(reverseMappedConcretePropertiesInference.ts, 4, 21)) +>barArg : Symbol(barArg, Decl(reverseMappedConcretePropertiesInference.ts, 5, 12)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 1, 3)) + + }; +}): T; +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 0, 22)) + +const res = test({ +>res : Symbol(res, Decl(reverseMappedConcretePropertiesInference.ts, 9, 5)) +>test : Symbol(test, Decl(reverseMappedConcretePropertiesInference.ts, 0, 0)) + + a: { +>a : Symbol(a, Decl(reverseMappedConcretePropertiesInference.ts, 9, 18)) + + foo: 'answer', +>foo : Symbol(foo, Decl(reverseMappedConcretePropertiesInference.ts, 10, 8)) + + onFoo: (arg) => arg.length, +>onFoo : Symbol(onFoo, Decl(reverseMappedConcretePropertiesInference.ts, 11, 22)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 12, 16)) +>arg.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 12, 16)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + + bar: 42, +>bar : Symbol(bar, Decl(reverseMappedConcretePropertiesInference.ts, 12, 35)) + + onBar: (arg) => arg + 10 +>onBar : Symbol(onBar, Decl(reverseMappedConcretePropertiesInference.ts, 13, 16)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 14, 16)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 14, 16)) + + }, + b: { +>b : Symbol(b, Decl(reverseMappedConcretePropertiesInference.ts, 15, 6)) + + foo: true, +>foo : Symbol(foo, Decl(reverseMappedConcretePropertiesInference.ts, 16, 8)) + + onFoo: (arg) => !!arg, +>onFoo : Symbol(onFoo, Decl(reverseMappedConcretePropertiesInference.ts, 17, 18)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 18, 16)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 18, 16)) + + bar: [1, 2, 3], +>bar : Symbol(bar, Decl(reverseMappedConcretePropertiesInference.ts, 18, 30)) + + onBar: (arg) => [arg, arg] +>onBar : Symbol(onBar, Decl(reverseMappedConcretePropertiesInference.ts, 19, 23)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 20, 16)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 20, 16)) +>arg : Symbol(arg, Decl(reverseMappedConcretePropertiesInference.ts, 20, 16)) + + }, +}) + +interface QueryFunctionContext { +>QueryFunctionContext : Symbol(QueryFunctionContext, Decl(reverseMappedConcretePropertiesInference.ts, 22, 2)) +>TQueryKey : Symbol(TQueryKey, Decl(reverseMappedConcretePropertiesInference.ts, 24, 31)) + + queryKey: TQueryKey; +>queryKey : Symbol(QueryFunctionContext.queryKey, Decl(reverseMappedConcretePropertiesInference.ts, 24, 58)) +>TQueryKey : Symbol(TQueryKey, Decl(reverseMappedConcretePropertiesInference.ts, 24, 31)) +} + +type QueryOptions = { +>QueryOptions : Symbol(QueryOptions, Decl(reverseMappedConcretePropertiesInference.ts, 26, 1)) + + key: string; +>key : Symbol(key, Decl(reverseMappedConcretePropertiesInference.ts, 28, 21)) + + fnData?: unknown; +>fnData : Symbol(fnData, Decl(reverseMappedConcretePropertiesInference.ts, 29, 14)) + +}; + +type UseQueriesOptions> = { +>UseQueriesOptions : Symbol(UseQueriesOptions, Decl(reverseMappedConcretePropertiesInference.ts, 31, 2)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --) ... and 3 more) +>QueryOptions : Symbol(QueryOptions, Decl(reverseMappedConcretePropertiesInference.ts, 26, 1)) + + [K in keyof T]: { +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 34, 3)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) + + queryKey: T[K]["key"]; +>queryKey : Symbol(queryKey, Decl(reverseMappedConcretePropertiesInference.ts, 34, 19)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 34, 3)) + + queryFn?: ( +>queryFn : Symbol(queryFn, Decl(reverseMappedConcretePropertiesInference.ts, 35, 26)) + + ctx: QueryFunctionContext +>ctx : Symbol(ctx, Decl(reverseMappedConcretePropertiesInference.ts, 36, 15)) +>QueryFunctionContext : Symbol(QueryFunctionContext, Decl(reverseMappedConcretePropertiesInference.ts, 22, 2)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 34, 3)) + + ) => Promise | T[K]["fnData"]; +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 34, 3)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 33, 23)) +>K : Symbol(K, Decl(reverseMappedConcretePropertiesInference.ts, 34, 3)) + + }; +}; + +declare function useQueries>( +>useQueries : Symbol(useQueries, Decl(reverseMappedConcretePropertiesInference.ts, 40, 2)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 42, 28)) +>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2016.array.include.d.ts, --, --) ... and 3 more) +>QueryOptions : Symbol(QueryOptions, Decl(reverseMappedConcretePropertiesInference.ts, 26, 1)) + + queries: [...UseQueriesOptions] +>queries : Symbol(queries, Decl(reverseMappedConcretePropertiesInference.ts, 42, 67)) +>UseQueriesOptions : Symbol(UseQueriesOptions, Decl(reverseMappedConcretePropertiesInference.ts, 31, 2)) +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 42, 28)) + +): T; +>T : Symbol(T, Decl(reverseMappedConcretePropertiesInference.ts, 42, 28)) + +const resQueries = useQueries([ +>resQueries : Symbol(resQueries, Decl(reverseMappedConcretePropertiesInference.ts, 46, 5)) +>useQueries : Symbol(useQueries, Decl(reverseMappedConcretePropertiesInference.ts, 40, 2)) + { + queryKey: "users", +>queryKey : Symbol(queryKey, Decl(reverseMappedConcretePropertiesInference.ts, 47, 3)) + + queryFn: (key) => [{ name: "Andarist" }], +>queryFn : Symbol(queryFn, Decl(reverseMappedConcretePropertiesInference.ts, 48, 22)) +>key : Symbol(key, Decl(reverseMappedConcretePropertiesInference.ts, 49, 14)) +>name : Symbol(name, Decl(reverseMappedConcretePropertiesInference.ts, 49, 24)) + + }, + { + queryKey: "posts", +>queryKey : Symbol(queryKey, Decl(reverseMappedConcretePropertiesInference.ts, 51, 3)) + + queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), +>queryFn : Symbol(queryFn, Decl(reverseMappedConcretePropertiesInference.ts, 52, 22)) +>key : Symbol(key, Decl(reverseMappedConcretePropertiesInference.ts, 53, 14)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>title : Symbol(title, Decl(reverseMappedConcretePropertiesInference.ts, 53, 40)) + } +]); + diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types new file mode 100644 index 0000000000000..1619fda245c67 --- /dev/null +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types @@ -0,0 +1,179 @@ +=== tests/cases/compiler/reverseMappedConcretePropertiesInference.ts === +declare function test>(a: { +>test : >(a: { [K in keyof T]: { foo: T[K]["foo"]; onFoo: (fooArg: T[K]["foo"]) => void; bar: T[K]["bar"]; onBar: (barArg: T[K]["bar"]) => void; }; }) => T +>foo : unknown +>bar : unknown +>a : { [K in keyof T]: { foo: T[K]["foo"]; onFoo: (fooArg: T[K]["foo"]) => void; bar: T[K]["bar"]; onBar: (barArg: T[K]["bar"]) => void; }; } + + [K in keyof T]: { + foo: T[K]["foo"]; +>foo : T[K]["foo"] + + onFoo: (fooArg: T[K]["foo"]) => void; +>onFoo : (fooArg: T[K]["foo"]) => void +>fooArg : T[K]["foo"] + + bar: T[K]["bar"]; +>bar : T[K]["bar"] + + onBar: (barArg: T[K]["bar"]) => void; +>onBar : (barArg: T[K]["bar"]) => void +>barArg : T[K]["bar"] + + }; +}): T; + +const res = test({ +>res : { a: { foo: string; bar: number; }; b: { foo: boolean; bar: number[]; }; } +>test({ a: { foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 }, b: { foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] },}) : { a: { foo: string; bar: number; }; b: { foo: boolean; bar: number[]; }; } +>test : >(a: { [K in keyof T]: { foo: T[K]["foo"]; onFoo: (fooArg: T[K]["foo"]) => void; bar: T[K]["bar"]; onBar: (barArg: T[K]["bar"]) => void; }; }) => T +>{ a: { foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 }, b: { foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] },} : { a: { foo: string; onFoo: (arg: string) => number; bar: number; onBar: (arg: number) => number; }; b: { foo: true; onFoo: (arg: boolean) => boolean; bar: number[]; onBar: (arg: number[]) => number[][]; }; } + + a: { +>a : { foo: string; onFoo: (arg: string) => number; bar: number; onBar: (arg: number) => number; } +>{ foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 } : { foo: string; onFoo: (arg: string) => number; bar: number; onBar: (arg: number) => number; } + + foo: 'answer', +>foo : string +>'answer' : "answer" + + onFoo: (arg) => arg.length, +>onFoo : (arg: string) => number +>(arg) => arg.length : (arg: string) => number +>arg : string +>arg.length : number +>arg : string +>length : number + + bar: 42, +>bar : number +>42 : 42 + + onBar: (arg) => arg + 10 +>onBar : (arg: number) => number +>(arg) => arg + 10 : (arg: number) => number +>arg : number +>arg + 10 : number +>arg : number +>10 : 10 + + }, + b: { +>b : { foo: true; onFoo: (arg: boolean) => boolean; bar: number[]; onBar: (arg: number[]) => number[][]; } +>{ foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] } : { foo: true; onFoo: (arg: boolean) => boolean; bar: number[]; onBar: (arg: number[]) => number[][]; } + + foo: true, +>foo : true +>true : true + + onFoo: (arg) => !!arg, +>onFoo : (arg: boolean) => boolean +>(arg) => !!arg : (arg: boolean) => boolean +>arg : boolean +>!!arg : boolean +>!arg : boolean +>arg : boolean + + bar: [1, 2, 3], +>bar : number[] +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + onBar: (arg) => [arg, arg] +>onBar : (arg: number[]) => number[][] +>(arg) => [arg, arg] : (arg: number[]) => number[][] +>arg : number[] +>[arg, arg] : number[][] +>arg : number[] +>arg : number[] + + }, +}) + +interface QueryFunctionContext { + queryKey: TQueryKey; +>queryKey : TQueryKey +} + +type QueryOptions = { +>QueryOptions : { key: string; fnData?: unknown; } + + key: string; +>key : string + + fnData?: unknown; +>fnData : unknown + +}; + +type UseQueriesOptions> = { +>UseQueriesOptions : UseQueriesOptions + + [K in keyof T]: { + queryKey: T[K]["key"]; +>queryKey : T[K]["key"] + + queryFn?: ( +>queryFn : ((ctx: QueryFunctionContext) => Promise | T[K]["fnData"]) | undefined + + ctx: QueryFunctionContext +>ctx : QueryFunctionContext + + ) => Promise | T[K]["fnData"]; + }; +}; + +declare function useQueries>( +>useQueries : (queries: [...UseQueriesOptions]) => T + + queries: [...UseQueriesOptions] +>queries : [...UseQueriesOptions] + +): T; + +const resQueries = useQueries([ +>resQueries : [{ key: "users"; fnData: { name: string; }[]; }, { key: "posts"; fnData: { title: string; }[]; }] +>useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : [{ key: "users"; fnData: { name: string; }[]; }, { key: "posts"; fnData: { title: string; }[]; }] +>useQueries : (queries: [...UseQueriesOptions]) => T +>[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : [{ queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; }, { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; }] + { +>{ queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], } : { queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; } + + queryKey: "users", +>queryKey : "users" +>"users" : "users" + + queryFn: (key) => [{ name: "Andarist" }], +>queryFn : (key: QueryFunctionContext<"users">) => { name: string; }[] +>(key) => [{ name: "Andarist" }] : (key: QueryFunctionContext<"users">) => { name: string; }[] +>key : QueryFunctionContext<"users"> +>[{ name: "Andarist" }] : { name: string; }[] +>{ name: "Andarist" } : { name: string; } +>name : string +>"Andarist" : "Andarist" + + }, + { +>{ queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), } : { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; } + + queryKey: "posts", +>queryKey : "posts" +>"posts" : "posts" + + queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), +>queryFn : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> +>(key) => Promise.resolve([{ title: 'TS 5.1' }]) : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> +>key : QueryFunctionContext<"posts"> +>Promise.resolve([{ title: 'TS 5.1' }]) : Promise<{ title: string; }[]> +>Promise.resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +>Promise : PromiseConstructor +>resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } +>[{ title: 'TS 5.1' }] : { title: string; }[] +>{ title: 'TS 5.1' } : { title: string; } +>title : string +>'TS 5.1' : "TS 5.1" + } +]); + diff --git a/tests/cases/compiler/reverseMappedConcretePropertiesInference.ts b/tests/cases/compiler/reverseMappedConcretePropertiesInference.ts new file mode 100644 index 0000000000000..d5a13fe36d5b9 --- /dev/null +++ b/tests/cases/compiler/reverseMappedConcretePropertiesInference.ts @@ -0,0 +1,60 @@ +// @strict: true +// @lib: esnext +// @noEmit: true + +declare function test>(a: { + [K in keyof T]: { + foo: T[K]["foo"]; + onFoo: (fooArg: T[K]["foo"]) => void; + bar: T[K]["bar"]; + onBar: (barArg: T[K]["bar"]) => void; + }; +}): T; + +const res = test({ + a: { + foo: 'answer', + onFoo: (arg) => arg.length, + bar: 42, + onBar: (arg) => arg + 10 + }, + b: { + foo: true, + onFoo: (arg) => !!arg, + bar: [1, 2, 3], + onBar: (arg) => [arg, arg] + }, +}) + +interface QueryFunctionContext { + queryKey: TQueryKey; +} + +type QueryOptions = { + key: string; + fnData?: unknown; +}; + +type UseQueriesOptions> = { + [K in keyof T]: { + queryKey: T[K]["key"]; + queryFn?: ( + ctx: QueryFunctionContext + ) => Promise | T[K]["fnData"]; + }; +}; + +declare function useQueries>( + queries: [...UseQueriesOptions] +): T; + +const resQueries = useQueries([ + { + queryKey: "users", + queryFn: (key) => [{ name: "Andarist" }], + }, + { + queryKey: "posts", + queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), + } +]); From d0e8b8b4b9e49447921d7c1646c92488bccb66bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 18 Apr 2023 00:39:30 +0200 Subject: [PATCH 08/18] update baselines, note: some are broken --- src/harness/fourslashInterfaceImpl.ts | 1 + .../reference/correlatedUnions.types | 10 +- .../indexAccessCombinedInference.errors.txt | 130 +++++++++ .../indexAccessCombinedInference.symbols | 7 +- .../indexAccessCombinedInference.types | 107 ++----- .../baselines/reference/inferingFromAny.types | 2 +- .../reference/keyofAndIndexedAccess.types | 6 +- ...actDefaultPropsInferenceSuccess.errors.txt | 63 +---- .../reactDefaultPropsInferenceSuccess.types | 32 +-- .../thisTypeInObjectLiterals2.errors.txt | 260 ++++++++++++++++++ .../reference/thisTypeInObjectLiterals2.js | 5 - .../thisTypeInObjectLiterals2.symbols | 8 +- .../reference/thisTypeInObjectLiterals2.types | 32 +-- .../reference/typeInferenceOnIndexUnion.types | 13 +- 14 files changed, 470 insertions(+), 206 deletions(-) create mode 100644 tests/baselines/reference/indexAccessCombinedInference.errors.txt create mode 100644 tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index d7ad6357b83c9..2e5a2738b83c3 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -1191,6 +1191,7 @@ export namespace Completion { typeEntry("Capitalize"), typeEntry("Uncapitalize"), interfaceEntry("ThisType"), + typeEntry("PartialInference"), varEntry("ArrayBuffer"), interfaceEntry("ArrayBufferTypes"), typeEntry("ArrayBufferLike"), diff --git a/tests/baselines/reference/correlatedUnions.types b/tests/baselines/reference/correlatedUnions.types index 4f80a4250d1e2..dc7077f294f18 100644 --- a/tests/baselines/reference/correlatedUnions.types +++ b/tests/baselines/reference/correlatedUnions.types @@ -489,7 +489,7 @@ function ff1() { >Keys : keyof { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; } const funs: { [P in Keys]: (...args: ArgMap[P]) => void } = { ->funs : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; } +>funs : { sum: (a: number, b: number) => void; concat: (a: string, b: string, c: string) => void; } >args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[P] >{ sum: (a, b) => a + b, concat: (a, b, c) => a + b + c } : { sum: (a: number, b: number) => number; concat: (a: string, b: string, c: string) => string; } @@ -520,14 +520,14 @@ function ff1() { >args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K] const fn = funs[funKey]; ->fn : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] ->funs[funKey] : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] ->funs : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; } +>fn : { sum: (a: number, b: number) => void; concat: (a: string, b: string, c: string) => void; }[K] +>funs[funKey] : { sum: (a: number, b: number) => void; concat: (a: string, b: string, c: string) => void; }[K] +>funs : { sum: (a: number, b: number) => void; concat: (a: string, b: string, c: string) => void; } >funKey : K fn(...args); >fn(...args) : void ->fn : { concat: (a: string, b: string, c: string) => void; sum: (a: number, b: number) => void; }[K] +>fn : { sum: (a: number, b: number) => void; concat: (a: string, b: string, c: string) => void; }[K] >...args : string | number >args : { sum: [a: number, b: number]; concat: [a: string, b: string, c: string]; }[K] } diff --git a/tests/baselines/reference/indexAccessCombinedInference.errors.txt b/tests/baselines/reference/indexAccessCombinedInference.errors.txt new file mode 100644 index 0000000000000..56bd3f9e28e35 --- /dev/null +++ b/tests/baselines/reference/indexAccessCombinedInference.errors.txt @@ -0,0 +1,130 @@ +tests/cases/compiler/indexAccessCombinedInference.ts(104,21): error TS2536: Type '"0"' cannot be used to index type 'this'. +tests/cases/compiler/indexAccessCombinedInference.ts(106,49): error TS2536: Type '"0"' cannot be used to index type 'T'. +tests/cases/compiler/indexAccessCombinedInference.ts(106,60): error TS2536: Type '"0"' cannot be used to index type 'T'. +tests/cases/compiler/indexAccessCombinedInference.ts(106,77): error TS2536: Type '"1"' cannot be used to index type 'T'. +tests/cases/compiler/indexAccessCombinedInference.ts(106,86): error TS2536: Type '"0"' cannot be used to index type 'T'. +tests/cases/compiler/indexAccessCombinedInference.ts(106,110): error TS2536: Type '"1"' cannot be used to index type 'T'. + + +==== tests/cases/compiler/indexAccessCombinedInference.ts (6 errors) ==== + // Simple case + interface Args { + TA: object, + TY: object + } + + declare function foo( + a: T["TA"], + b: T["TY"]): T["TA"] & T["TY"]; + + const x = foo({ + x: { + j: 12, + i: 11 + } + }, { y: 42 }); + + // Union result type + interface A { + foo: number; + } + interface B { + bar: string; + } + declare const something: A | B; + + const y = foo(something, { bat: 42 }); + + // Union key type + interface Args2 { + TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list + TB?: object, + TY: object + } + declare function foo2( + a: T["TA"] | T["TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; + declare function foo3( // Morally equivalent to foo2 + a: T["TA" | "TB"], + b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; + let z = foo2({ + x: { + j: 12, + i: 11 + } + }, { y: 42 }); + let zz = foo3({ + x: { + j: 12, + i: 11 + } + }, { y: 42 }); + z = zz; + zz = z; + + // Higher-order + interface Args3 { + Key: "A" | "B", + A: object, + B: object, + Merge: object, + } + declare const either: "A" | "B"; + declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; + + const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); + const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); + const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); + + const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); + const opt4 = pickDelayed("A"); + const opt5 = pickDelayed("B"); + const opt6 = pickDelayed(either); + + // Reopenable + interface Args3 { + /** + * One must make patched parameters optional, otherwise signatures expecting the unpatched + * interface (ie, pickOne above) will not be able to produce a type satisfying the interface + * (as there are no inference sites for the new members) and will fall back to the constraints on each member + */ + Extra?: object, + } + declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; + const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); + const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); + const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); + + // Interactions with `this` types + interface TPicker { + Key: keyof this, + X: number, + Y: string + } + declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; + const cx = chooseLiteral("X", 1, "no"); + const cy = chooseLiteral("Y", 0, "yes"); + const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); + const cneither = chooseLiteral("Key", 0, "no"); + + // Multiple inference sites + interface Args4 { + 0: object, + 1: Record, + ~~~~~~~~~ +!!! error TS2536: Type '"0"' cannot be used to index type 'this'. + } + declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; + ~~~~~~ +!!! error TS2536: Type '"0"' cannot be used to index type 'T'. + ~~~~~~ +!!! error TS2536: Type '"0"' cannot be used to index type 'T'. + ~~~~~~ +!!! error TS2536: Type '"1"' cannot be used to index type 'T'. + ~~~~~~ +!!! error TS2536: Type '"0"' cannot be used to index type 'T'. + ~~~~~~ +!!! error TS2536: Type '"1"' cannot be used to index type 'T'. + + const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); + \ No newline at end of file diff --git a/tests/baselines/reference/indexAccessCombinedInference.symbols b/tests/baselines/reference/indexAccessCombinedInference.symbols index d108b7d1e9c72..599b8f74394da 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.symbols +++ b/tests/baselines/reference/indexAccessCombinedInference.symbols @@ -345,9 +345,12 @@ interface Args4 { >Args4 : Symbol(Args4, Decl(indexAccessCombinedInference.ts, 98, 47)) 0: object, +>0 : Symbol(Args4[0], Decl(indexAccessCombinedInference.ts, 101, 17)) + 1: Record, ->Record : Symbol(Record, Decl(lib.d.ts, --, --)) ->Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>1 : Symbol(Args4[1], Decl(indexAccessCombinedInference.ts, 102, 14)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) } declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; >dualInputs : Symbol(dualInputs, Decl(indexAccessCombinedInference.ts, 104, 1)) diff --git a/tests/baselines/reference/indexAccessCombinedInference.types b/tests/baselines/reference/indexAccessCombinedInference.types index 03badaade7613..523749d82c9ac 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.types +++ b/tests/baselines/reference/indexAccessCombinedInference.types @@ -1,8 +1,6 @@ === tests/cases/compiler/indexAccessCombinedInference.ts === // Simple case interface Args { ->Args : Args - TA: object, >TA : object @@ -12,18 +10,12 @@ interface Args { declare function foo( >foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] ->T : T ->Args : Args a: T["TA"], >a : T["TA"] ->T : T b: T["TY"]): T["TA"] & T["TY"]; >b : T["TY"] ->T : T ->T : T ->T : T const x = foo({ >x : { x: { j: number; i: number; }; } & { y: number; } @@ -50,25 +42,19 @@ const x = foo({ // Union result type interface A { ->A : A - foo: number; >foo : number } interface B { ->B : B - bar: string; >bar : string } declare const something: A | B; >something : A | B ->A : A ->B : B const y = foo(something, { bat: 42 }); >y : (A & { bat: number; }) | (B & { bat: number; }) ->foo(something, { bat: 42 }) : (A & { bat: number; }) | (B & { bat: number; }) +>foo(something, { bat: 42 }) : (A | B) & { bat: number; } >foo : (a: T["TA"], b: T["TY"]) => T["TA"] & T["TY"] >something : A | B >{ bat: 42 } : { bat: number; } @@ -77,8 +63,6 @@ const y = foo(something, { bat: 42 }); // Union key type interface Args2 { ->Args2 : Args2 - TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list >TA : object @@ -89,41 +73,26 @@ interface Args2 { >TY : object } declare function foo2( ->foo2 : (a: T["TA"] | T["TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] ->T : T ->Args2 : Args2 +>foo2 : (a: T["TA"] | T["TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"];} & T["TY"] a: T["TA"] | T["TB"], >a : T["TA"] | T["TB"] ->T : T ->T : T b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; >b : T["TY"] ->T : T >a : T["TA"] ->T : T >b : T["TB"] ->T : T ->T : T declare function foo3( // Morally equivalent to foo2 ->foo3 : (a: T["TA" | "TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] ->T : T ->Args2 : Args2 +>foo3 : (a: T["TA" | "TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"];} & T["TY"] a: T["TA" | "TB"], >a : T["TA" | "TB"] ->T : T b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; >b : T["TY"] ->T : T >a : T["TA"] ->T : T >b : T["TB"] ->T : T ->T : T let z = foo2({ >z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } @@ -149,8 +118,8 @@ let z = foo2({ >42 : 42 let zz = foo3({ ->zz : { a: any; b: any; } & { y: number; } ->foo3({ x: { j: 12, i: 11 }}, { y: 42 }) : { a: any; b: any; } & { y: number; } +>zz : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } +>foo3({ x: { j: 12, i: 11 }}, { y: 42 }) : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } >foo3 : (a: T["TA" | "TB"], b: T["TY"]) => { a: T["TA"]; b: T["TB"]; } & T["TY"] >{ x: { j: 12, i: 11 }} : { x: { j: number; i: number; }; } @@ -172,19 +141,17 @@ let zz = foo3({ >42 : 42 z = zz; ->z = zz : { a: any; b: any; } & { y: number; } +>z = zz : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } >z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } ->zz : { a: any; b: any; } & { y: number; } +>zz : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } zz = z; >zz = z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } ->zz : { a: any; b: any; } & { y: number; } +>zz : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } >z : { a: { x: { j: number; i: number; }; }; b: { x: { j: number; i: number; }; }; } & { y: number; } // Higher-order interface Args3 { ->Args3 : Args3 - Key: "A" | "B", >Key : "A" | "B" @@ -202,19 +169,10 @@ declare const either: "A" | "B"; declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; >pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } ->T : T ->Args3 : Args3 >key : T["Key"] ->T : T >left : T["A"] ->T : T >right : T["B"] ->T : T >into : T["Merge"] ->T : T ->T : T ->T : T ->T : T const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); >opt1 : { x: number; } & { z: RegExp; } @@ -248,7 +206,7 @@ const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); >opt3 : ({ x: number; } & { z: RegExp; }) | ({ y: string; } & { z: RegExp; }) ->pickOne(either, {x: 12}, {y: ""}, {z: /./}) : ({ x: number; } & { z: RegExp; }) | ({ y: string; } & { z: RegExp; }) +>pickOne(either, {x: 12}, {y: ""}, {z: /./}) : ({ x: number; } | { y: string; }) & { z: RegExp; } >pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } >either : "A" | "B" >{x: 12} : { x: number; } @@ -264,10 +222,7 @@ const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); >pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } >(x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}) : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } ->TKey : TKey ->Args3 : Args3 >x : TKey ->TKey : TKey >pickOne(x, {j: x}, {i: x}, {chosen: x}) : ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } >pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } >x : TKey @@ -295,14 +250,12 @@ const opt5 = pickDelayed("B"); const opt6 = pickDelayed(either); >opt6 : ({ j: "A" | "B"; } & { chosen: "A" | "B"; }) | ({ i: "A" | "B"; } & { chosen: "A" | "B"; }) ->pickDelayed(either) : ({ j: "A" | "B"; } & { chosen: "A" | "B"; }) | ({ i: "A" | "B"; } & { chosen: "A" | "B"; }) +>pickDelayed(either) : ({ j: "A" | "B"; } | { i: "A" | "B"; }) & { chosen: "A" | "B"; } >pickDelayed : (x: TKey) => ({ Key: TKey; } & { A: { j: TKey; }; } & { B: { i: TKey; }; } & { Merge: { chosen: TKey; }; })[TKey] & { chosen: TKey; } >either : "A" | "B" // Reopenable interface Args3 { ->Args3 : Args3 - /** * One must make patched parameters optional, otherwise signatures expecting the unpatched * interface (ie, pickOne above) will not be able to produce a type satisfying the interface @@ -312,25 +265,14 @@ interface Args3 { >Extra : object } declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; ->pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } ->T : T ->Args3 : Args3 +>pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"];}; } >key : T["Key"] ->T : T >left : T["A"] ->T : T >right : T["B"] ->T : T >into : T["Merge"] ->T : T >extra : T["Extra"] ->T : T ->T : T ->T : T >into : T["Merge"] ->T : T >extra : T["Extra"] ->T : T const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); >opt7 : { x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; } @@ -370,7 +312,7 @@ const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); >opt9 : ({ x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) | ({ y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) ->pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}) : ({ x: number; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) | ({ y: string; } & { into: { z: RegExp; }; extra: { z: RegExp; }; }) +>pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}) : ({ x: number; } | { y: string; }) & { into: { z: RegExp; }; extra: { z: RegExp; }; } >pickOne : { (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; (key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & { into: T["Merge"]; extra: T["Extra"]; }; } >either : "A" | "B" >{x: 12} : { x: number; } @@ -388,8 +330,6 @@ const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); // Interactions with `this` types interface TPicker { ->TPicker : TPicker - Key: keyof this, >Key : keyof this @@ -401,16 +341,9 @@ interface TPicker { } declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; >chooseLiteral : (choice: T["Key"], x: T["X"], y: T["Y"]) => T[T["Key"]] ->T : T ->TPicker : TPicker >choice : T["Key"] ->T : T >x : T["X"] ->T : T >y : T["Y"] ->T : T ->T : T ->T : T const cx = chooseLiteral("X", 1, "no"); >cx : 1 @@ -447,26 +380,18 @@ const cneither = chooseLiteral("Key", 0, "no"); // Multiple inference sites interface Args4 { ->Args4 : Args4 - 0: object, +>0 : object + 1: Record, ->Record : Record ->Function : Function +>1 : Record } declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; ->dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"]; } ->T : T ->Args4 : Args4 +>dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"];} >x : T["0"] ->T : T >y : T["0"] ->T : T >toDelay : T["1"] ->T : T ->T : T >transformers : T["1"] ->T : T const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); >result : { x: number; } & { x: number; } & { transformers: { x: () => ""; }; } diff --git a/tests/baselines/reference/inferingFromAny.types b/tests/baselines/reference/inferingFromAny.types index 817287ed8180c..29782e569f84c 100644 --- a/tests/baselines/reference/inferingFromAny.types +++ b/tests/baselines/reference/inferingFromAny.types @@ -203,7 +203,7 @@ var a = f18(a); var a = f19(a, a); >a : any ->f19(a, a) : { [K in Keys]: { [K1 in K]: any; }; }[K] +>f19(a, a) : PartialInference >f19 : (k: K, x: T[K]) => T >a : any >a : any diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index e8d59f2fa7978..5a6e943ed2d2a 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -58,10 +58,10 @@ type K00 = keyof any; // string >K00 : string | number | symbol type K01 = keyof string; // "toString" | "charAt" | ... ->K01 : number | "length" | "toString" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf" +>K01 : number | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "toString" | "valueOf" type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ... ->K02 : "toString" | "toLocaleString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" +>K02 : "toFixed" | "toExponential" | "toPrecision" | "toString" | "toLocaleString" | "valueOf" type K03 = keyof boolean; // "valueOf" >K03 : "valueOf" @@ -97,7 +97,7 @@ type K14 = keyof Object; // "constructor" | "toString" | ... >K14 : keyof Object type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ... ->K15 : "toString" | "toLocaleString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" +>K15 : "toFixed" | "toExponential" | "toPrecision" | "toString" | "toLocaleString" | "valueOf" type K16 = keyof [string, number]; // "0" | "1" | "length" | "toString" | ... >K16 : keyof [string, number] diff --git a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt index 335f0febc862f..3c3962fa6ae8d 100644 --- a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt +++ b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt @@ -1,26 +1,10 @@ -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,36): error TS2769: No overload matches this call. - Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. - Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. - Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. - Type 'void' is not assignable to type 'boolean'. - Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. - Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(43,41): error TS2769: No overload matches this call. - Overload 1 of 2, '(props: Readonly): FieldFeedbackBeta', gave the following error. - Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. - Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. - Type 'void' is not assignable to type 'boolean'. - Overload 2 of 2, '(props: Props, context?: any): FieldFeedbackBeta', gave the following error. - Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: No overload matches this call. - Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. - Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. - Type 'void' is not assignable to type 'boolean'. - Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. - Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(24,42): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,42): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(61,43): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: Parameter 'value' implicitly has an 'any' type. -==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (3 errors) ==== +==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (4 errors) ==== /// import React from 'react'; @@ -45,19 +29,13 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: // OK const Test1 = () => !!value} />; + ~~~~~ +!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // Error: Void not assignable to boolean const Test2 = () => console.log(value)} />; - ~~~~ -!!! error TS2769: No overload matches this call. -!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. -!!! error TS2769: Type 'void' is not assignable to type 'boolean'. -!!! error TS2769: Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' + ~~~~~ +!!! error TS7006: Parameter 'value' implicitly has an 'any' type. class FieldFeedbackBeta

extends React.Component

{ static defaultProps: BaseProps = { @@ -74,16 +52,6 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: // Error: Void not assignable to boolean const Test2a = () => console.log(value)} error>Hah; - ~~~~ -!!! error TS2769: No overload matches this call. -!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedbackBeta', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. -!!! error TS2769: Type 'void' is not assignable to type 'boolean'. -!!! error TS2769: Overload 2 of 2, '(props: Props, context?: any): FieldFeedbackBeta', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children"> & Partial & Readonly, keyof Props>> & Partial>' -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children"> & Partial & Readonly, keyof Props>> & Partial>' interface MyPropsProps extends Props { when: (value: string) => boolean; @@ -102,18 +70,13 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: // OK const Test3 = () => !!value} />; + ~~~~~ +!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // Error: Void not assignable to boolean const Test4 = () => console.log(value)} />; - ~~~~ -!!! error TS2769: No overload matches this call. -!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. -!!! error TS2769: Type 'void' is not assignable to type 'boolean'. -!!! error TS2769: Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. -!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' -!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' + ~~~~~ +!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // OK const Test5 = () => ; diff --git a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types index 9995e298150c5..289c831bc55a1 100644 --- a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types +++ b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types @@ -49,12 +49,12 @@ const Test1 = () => !!value} />; >() => !!value} /> : () => JSX.Element > !!value} /> : JSX.Element >FieldFeedback : typeof FieldFeedback ->when : (value: string) => boolean ->value => !!value : (value: string) => boolean ->value : string +>when : (value: any) => boolean +>value => !!value : (value: any) => boolean +>value : any >!!value : boolean >!value : boolean ->value : string +>value : any // Error: Void not assignable to boolean const Test2 = () => console.log(value)} />; @@ -62,14 +62,14 @@ const Test2 = () => console.log(value)} />; >() => console.log(value)} /> : () => JSX.Element > console.log(value)} /> : JSX.Element >FieldFeedback : typeof FieldFeedback ->when : (value: string) => void ->value => console.log(value) : (value: string) => void ->value : string +>when : (value: any) => void +>value => console.log(value) : (value: any) => void +>value : any >console.log(value) : void >console.log : (...data: any[]) => void >console : Console >log : (...data: any[]) => void ->value : string +>value : any class FieldFeedbackBeta

extends React.Component

{ >FieldFeedbackBeta : FieldFeedbackBeta

@@ -176,12 +176,12 @@ const Test3 = () => !!value} />; >() => !!value} /> : () => JSX.Element > !!value} /> : JSX.Element >FieldFeedback2 : typeof FieldFeedback2 ->when : (value: string) => boolean ->value => !!value : (value: string) => boolean ->value : string +>when : (value: any) => boolean +>value => !!value : (value: any) => boolean +>value : any >!!value : boolean >!value : boolean ->value : string +>value : any // Error: Void not assignable to boolean const Test4 = () => console.log(value)} />; @@ -189,14 +189,14 @@ const Test4 = () => console.log(value)} />; >() => console.log(value)} /> : () => JSX.Element > console.log(value)} /> : JSX.Element >FieldFeedback2 : typeof FieldFeedback2 ->when : (value: string) => void ->value => console.log(value) : (value: string) => void ->value : string +>when : (value: any) => void +>value => console.log(value) : (value: any) => void +>value : any >console.log(value) : void >console.log : (...data: any[]) => void >console : Console >log : (...data: any[]) => void ->value : string +>value : any // OK const Test5 = () => ; diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt b/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt new file mode 100644 index 0000000000000..b00cc7e423fb5 --- /dev/null +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt @@ -0,0 +1,260 @@ +tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(224,9): error TS2322: Type '{ test(): number; hello: { get(): string; set(value: string): void; }; }' is not assignable to type 'Accessors<{ hello: string; }>'. + Object literal may only specify known properties, and 'test' does not exist in type 'Accessors<{ hello: string; }>'. +tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(239,5): error TS2339: Property 'f' does not exist on type '{ x: number; y: number; }'. +tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(240,5): error TS2339: Property 'test' does not exist on type '{ x: number; y: number; }'. +tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(241,5): error TS2339: Property 'hello' does not exist on type '{ x: number; y: number; }'. + + +==== tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts (4 errors) ==== + // In methods of an object literal with no contextual type, 'this' has the type + // of the object literal. + + let obj1 = { + a: 1, + f() { + return this.a; + }, + b: "hello", + c: { + g() { + this.g(); + } + }, + get d() { + return this.a; + }, + get e() { + return this.b; + }, + set e(value) { + this.b = value; + } + }; + + // In methods of an object literal with a contextual type, 'this' has the + // contextual type. + + type Point = { + x: number; + y: number; + z?: number; + moveBy(dx: number, dy: number, dz?: number): void; + } + + let p1: Point = { + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }; + + let p2: Point | null = { + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }; + + let p3: Point | undefined = { + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }; + + let p4: Point | null | undefined = { + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }; + + declare function f1(p: Point): void; + + f1({ + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }); + + declare function f2(p: Point | null | undefined): void; + + f2({ + x: 10, + y: 20, + moveBy(dx, dy, dz) { + this.x += dx; + this.y += dy; + if (this.z && dz) { + this.z += dz; + } + } + }); + + // In methods of an object literal with a contextual type that includes some + // ThisType, 'this' is of type T. + + type ObjectDescriptor = { + data?: D; + methods?: M & ThisType; // Type of 'this' in methods is D & M + } + + declare function makeObject(desc: ObjectDescriptor): D & M; + + let x1 = makeObject({ + data: { x: 0, y: 0 }, + methods: { + moveBy(dx: number, dy: number) { + this.x += dx; // Strongly typed this + this.y += dy; // Strongly typed this + } + } + }); + + // In methods contained in an object literal with a contextual type that includes + // some ThisType, 'this' is of type T. + + type ObjectDescriptor2 = ThisType & { + data?: D; + methods?: M; + } + + declare function makeObject2(desc: ObjectDescriptor): D & M; + + let x2 = makeObject2({ + data: { x: 0, y: 0 }, + methods: { + moveBy(dx: number, dy: number) { + this.x += dx; // Strongly typed this + this.y += dy; // Strongly typed this + } + } + }); + + // Check pattern similar to Object.defineProperty and Object.defineProperties + + type PropDesc = { + value?: T; + get?(): T; + set?(value: T): void; + } + + type PropDescMap = { + [K in keyof T]: PropDesc; + } + + declare function defineProp(obj: T, name: K, desc: PropDesc & ThisType): T & Record; + + declare function defineProps(obj: T, descs: PropDescMap & ThisType): T & U; + + let p10 = defineProp(p1, "foo", { value: 42 }); + p10.foo = p10.foo + 1; + + let p11 = defineProp(p1, "bar", { + get() { + return this.x; + }, + set(value: number) { + this.x = value; + } + }); + p11.bar = p11.bar + 1; + + let p12 = defineProps(p1, { + foo: { + value: 42 + }, + bar: { + get(): number { + return this.x; + }, + set(value: number) { + this.x = value; + } + } + }); + p12.foo = p12.foo + 1; + p12.bar = p12.bar + 1; + + // Proof of concept for typing of Vue.js + + type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; + + type Dictionary = { [x: string]: T } + + type Computed = { + get?(): T; + set?(value: T): void; + } + + type VueOptions = ThisType & { + data?: D | (() => D); + methods?: M; + computed?: Accessors

; + } + + declare const Vue: new (options: VueOptions) => D & M & P; + + let vue = new Vue({ + data: () => ({ x: 1, y: 2 }), + methods: { + f(x: string) { + return this.x; + } + }, + computed: { + test(): number { + ~~~~ +!!! error TS2322: Type '{ test(): number; hello: { get(): string; set(value: string): void; }; }' is not assignable to type 'Accessors<{ hello: string; }>'. +!!! error TS2322: Object literal may only specify known properties, and 'test' does not exist in type 'Accessors<{ hello: string; }>'. +!!! related TS6500 tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts:211:5: The expected type comes from property 'computed' which is declared here on type 'VueOptions<{ x: number; y: number; }, { f(x: string): number; }, { hello: string; }>' + return this.x; + }, + hello: { + get() { + return "hi"; + }, + set(value: string) { + } + } + } + }); + + vue; + vue.x; + vue.f("abc"); + ~ +!!! error TS2339: Property 'f' does not exist on type '{ x: number; y: number; }'. + vue.test; + ~~~~ +!!! error TS2339: Property 'test' does not exist on type '{ x: number; y: number; }'. + vue.hello; + ~~~~~ +!!! error TS2339: Property 'hello' does not exist on type '{ x: number; y: number; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.js b/tests/baselines/reference/thisTypeInObjectLiterals2.js index 320edfc61c31f..1114422c9c0ee 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.js +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.js @@ -484,9 +484,4 @@ declare const Vue: new (options: VueOptions) => D & M & P; declare let vue: { x: number; y: number; -} & { - f(x: string): number; -} & { - test: number; - hello: string; }; diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.symbols b/tests/baselines/reference/thisTypeInObjectLiterals2.symbols index 0ed97b942f1c5..313a9a619fa30 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.symbols +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.symbols @@ -726,6 +726,7 @@ let vue = new Vue({ return this.x; >this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) +>this : Symbol(__object, Decl(thisTypeInObjectLiterals2.ts, 216, 17)) >x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) } }, @@ -737,6 +738,7 @@ let vue = new Vue({ return this.x; >this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) +>this : Symbol(__object, Decl(thisTypeInObjectLiterals2.ts, 216, 17)) >x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) }, @@ -765,17 +767,11 @@ vue.x; >x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) vue.f("abc"); ->vue.f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 217, 14)) >vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) ->f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 217, 14)) vue.test; ->vue.test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 222, 15)) >vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) ->test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 222, 15)) vue.hello; ->vue.hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 225, 10)) >vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) ->hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 225, 10)) diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.types b/tests/baselines/reference/thisTypeInObjectLiterals2.types index a4e97906ecdde..1d00535f1fc89 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.types +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.types @@ -704,8 +704,8 @@ declare const Vue: new (options: VueOptions) => D & M & P; >options : VueOptions let vue = new Vue({ ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } ->new Vue({ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }}) : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>vue : { x: number; y: number; } +>new Vue({ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }}) : { x: number; y: number; } >Vue : new (options: VueOptions) => D & M & P >{ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }} : { data: () => { x: number; y: number; }; methods: { f(x: string): number; }; computed: { test(): number; hello: { get(): string; set(value: string): void; }; }; } @@ -729,7 +729,7 @@ let vue = new Vue({ return this.x; >this.x : number ->this : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>this : { x: number; y: number; } >x : number } }, @@ -742,7 +742,7 @@ let vue = new Vue({ return this.x; >this.x : number ->this : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>this : { x: number; y: number; } >x : number }, @@ -766,27 +766,27 @@ let vue = new Vue({ }); vue; ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>vue : { x: number; y: number; } vue.x; >vue.x : number ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>vue : { x: number; y: number; } >x : number vue.f("abc"); ->vue.f("abc") : number ->vue.f : (x: string) => number ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } ->f : (x: string) => number +>vue.f("abc") : any +>vue.f : any +>vue : { x: number; y: number; } +>f : any >"abc" : "abc" vue.test; ->vue.test : number ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } ->test : number +>vue.test : any +>vue : { x: number; y: number; } +>test : any vue.hello; ->vue.hello : string ->vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } ->hello : string +>vue.hello : any +>vue : { x: number; y: number; } +>hello : any diff --git a/tests/baselines/reference/typeInferenceOnIndexUnion.types b/tests/baselines/reference/typeInferenceOnIndexUnion.types index 57e171d86ba74..5ca5a9c604fed 100644 --- a/tests/baselines/reference/typeInferenceOnIndexUnion.types +++ b/tests/baselines/reference/typeInferenceOnIndexUnion.types @@ -1,6 +1,6 @@ === tests/cases/compiler/typeInferenceOnIndexUnion.ts === type Options = { k: "a", a: number } | { k: "b", b: string }; ->Options : Options +>Options : { k: "a"; a: number; } | { k: "b"; b: string; } >k : "a" >a : number >k : "b" @@ -8,11 +8,7 @@ type Options = { k: "a", a: number } | { k: "b", b: string }; declare function f(p: T["k"]): T; >f : (p: T["k"]) => T ->T : T ->Options : Options >p : T["k"] ->T : T ->T : T const x = f("a"); // expect it to be `{ k: "a", a: number }` >x : { k: "a"; a: number; } @@ -21,7 +17,7 @@ const x = f("a"); // expect it to be `{ k: "a", a: number }` >"a" : "a" type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; ->Options2 : Options2 +>Options2 : { k: "a"; a: number; c: {}; } | { k: "b"; b: string; c: {}; } >k : "a" >a : number >c : {} @@ -31,13 +27,8 @@ type Options2 = { k: "a", a: number, c: {} } | { k: "b", b: string, c: {} }; declare function f2(p: T["k"], c: T["c"]): T; >f2 : (p: T["k"], c: T["c"]) => T ->T : T ->Options2 : Options2 >p : T["k"] ->T : T >c : T["c"] ->T : T ->T : T const x2 = f2("a", { x: 1, y: 2 }); // expect it to be `{ k: "a", a: number, c: {x: number, y: number} }` >x2 : { k: "a"; c: { x: number; y: number; }; a: number; } From e5d0ea890bf928e6b9a76fd8a18a6d85145f0936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 18 Apr 2023 00:43:11 +0200 Subject: [PATCH 09/18] use new `inference.indexes` in reverse mapped type inference --- src/compiler/checker.ts | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f0e1b78dd8969..68a437a300584 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24106,21 +24106,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function inferReverseMappedType(sourceType: Type, target: MappedType, constraint: IndexType): Type { const typeParameter = getIndexedAccessType(constraint.type, getTypeParameterFromMappedType(target)) as TypeParameter; const templateType = getTemplateTypeFromMappedType(target); - const inferences = [createInferenceInfo(typeParameter)]; - inferTypes(inferences, sourceType, templateType); - const wholeInferredType = getTypeFromInference(inferences[0]); - if (!wholeInferredType && inferences.length > 1) { - const members = createSymbolTable(); - for (let i = 1; i < inferences.length; i++) { - const indexType = (inferences[i].typeParameter as IndexedAccessType).indexType as StringLiteralType; - const name = escapeLeadingUnderscores(indexType.value); - const prop = createSymbol(SymbolFlags.Property, name); - prop.links.type = getTypeFromInference(inferences[i]) || unknownType; - members.set(name, prop); - } - return createAnonymousType(undefined, members, emptyArray, emptyArray, emptyArray); - } - return wholeInferredType || unknownType; + const inference = createInferenceInfo(typeParameter); + inferTypes([inference], sourceType, templateType); + return getTypeFromInference(inference) || (inference.indexes ? getIntersectionType(inference.indexes) : unknownType); } function* getUnmatchedProperties(source: Type, target: Type, requireOptionalProperties: boolean, matchDiscriminantProperties: boolean): IterableIterator { @@ -24438,15 +24426,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isFromInferenceBlockedSource(source)) { return; } - let inference = getInferenceInfoForType(target); - if ( - !inference && (target.flags & TypeFlags.IndexedAccess) && - inferences[0].typeParameter.flags & TypeFlags.IndexedAccess && - (target as IndexedAccessType).indexType.flags & TypeFlags.StringLiteral && - ((target as IndexedAccessType).objectType.flags & TypeFlags.IndexedAccess) - ) { - inferences.push(inference = createInferenceInfo(target)); - } + const inference = getInferenceInfoForType(target); if (inference) { // If target is a type parameter, make an inference, unless the source type contains // a "non-inferrable" type. Types with this flag set are markers used to prevent inference. From b0b2fcb1ff2672ecae7afbf1d154663fdf51dadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 18 Apr 2023 09:16:12 +0200 Subject: [PATCH 10/18] update reverse mapped baseline --- ...rseMappedConcretePropertiesInference.types | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types index 1619fda245c67..31cb77a54c78c 100644 --- a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types @@ -24,8 +24,8 @@ declare function test>( }): T; const res = test({ ->res : { a: { foo: string; bar: number; }; b: { foo: boolean; bar: number[]; }; } ->test({ a: { foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 }, b: { foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] },}) : { a: { foo: string; bar: number; }; b: { foo: boolean; bar: number[]; }; } +>res : { a: { foo: string; } & { bar: number; }; b: { foo: boolean; } & { bar: number[]; }; } +>test({ a: { foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 }, b: { foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] },}) : { a: { foo: string; } & { bar: number; }; b: { foo: boolean; } & { bar: number[]; }; } >test : >(a: { [K in keyof T]: { foo: T[K]["foo"]; onFoo: (fooArg: T[K]["foo"]) => void; bar: T[K]["bar"]; onBar: (barArg: T[K]["bar"]) => void; }; }) => T >{ a: { foo: 'answer', onFoo: (arg) => arg.length, bar: 42, onBar: (arg) => arg + 10 }, b: { foo: true, onFoo: (arg) => !!arg, bar: [1, 2, 3], onBar: (arg) => [arg, arg] },} : { a: { foo: string; onFoo: (arg: string) => number; bar: number; onBar: (arg: number) => number; }; b: { foo: true; onFoo: (arg: boolean) => boolean; bar: number[]; onBar: (arg: number[]) => number[][]; }; } @@ -134,21 +134,21 @@ declare function useQueries>( ): T; const resQueries = useQueries([ ->resQueries : [{ key: "users"; fnData: { name: string; }[]; }, { key: "posts"; fnData: { title: string; }[]; }] ->useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : [{ key: "users"; fnData: { name: string; }[]; }, { key: "posts"; fnData: { title: string; }[]; }] +>resQueries : readonly QueryOptions[] +>useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : readonly QueryOptions[] >useQueries : (queries: [...UseQueriesOptions]) => T ->[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : [{ queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; }, { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; }] +>[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : ({ queryKey: string; queryFn: (key: QueryFunctionContext) => { name: string; }[]; } | { queryKey: string; queryFn: (key: QueryFunctionContext) => Promise<{ title: string; }[]>; })[] { ->{ queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], } : { queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; } +>{ queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], } : { queryKey: string; queryFn: (key: QueryFunctionContext) => { name: string; }[]; } queryKey: "users", ->queryKey : "users" +>queryKey : string >"users" : "users" queryFn: (key) => [{ name: "Andarist" }], ->queryFn : (key: QueryFunctionContext<"users">) => { name: string; }[] ->(key) => [{ name: "Andarist" }] : (key: QueryFunctionContext<"users">) => { name: string; }[] ->key : QueryFunctionContext<"users"> +>queryFn : (key: QueryFunctionContext) => { name: string; }[] +>(key) => [{ name: "Andarist" }] : (key: QueryFunctionContext) => { name: string; }[] +>key : QueryFunctionContext >[{ name: "Andarist" }] : { name: string; }[] >{ name: "Andarist" } : { name: string; } >name : string @@ -156,16 +156,16 @@ const resQueries = useQueries([ }, { ->{ queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), } : { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; } +>{ queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), } : { queryKey: string; queryFn: (key: QueryFunctionContext) => Promise<{ title: string; }[]>; } queryKey: "posts", ->queryKey : "posts" +>queryKey : string >"posts" : "posts" queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), ->queryFn : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> ->(key) => Promise.resolve([{ title: 'TS 5.1' }]) : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> ->key : QueryFunctionContext<"posts"> +>queryFn : (key: QueryFunctionContext) => Promise<{ title: string; }[]> +>(key) => Promise.resolve([{ title: 'TS 5.1' }]) : (key: QueryFunctionContext) => Promise<{ title: string; }[]> +>key : QueryFunctionContext >Promise.resolve([{ title: 'TS 5.1' }]) : Promise<{ title: string; }[]> >Promise.resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } >Promise : PromiseConstructor From 7fe118a17a3bc61cb12af93b7e84cb8f7c05388c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 18 Apr 2023 10:46:51 +0200 Subject: [PATCH 11/18] Call `getActualTypeVariable` appropriately to fix inference for a tuple reverse mapped type --- src/compiler/checker.ts | 2 +- ...ppedConcretePropertiesInference.errors.txt | 72 +++++++++++++++++++ ...rseMappedConcretePropertiesInference.types | 26 +++---- 3 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 68a437a300584..9879c62f8eada 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24513,7 +24513,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } else if (target.flags & TypeFlags.IndexedAccess) { const targetConstraint = (target as IndexedAccessType).objectType; - const inference = getInferenceInfoForType(targetConstraint); + const inference = getInferenceInfoForType(getActualTypeVariable(targetConstraint)); if (inference) { if (!inference.isFixed) { // Instantiates instance of `type PartialInference = ({[K in Keys]: {[K1 in K]: T}})[Keys];` diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt b/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt new file mode 100644 index 0000000000000..513353a6e7e45 --- /dev/null +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt @@ -0,0 +1,72 @@ +tests/cases/compiler/reverseMappedConcretePropertiesInference.ts(54,5): error TS2322: Type '(key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>' is not assignable to type '(ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. + Type 'Promise<{ title: string; }[]>' is not assignable to type '({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. + Type 'Promise<{ title: string; }[]>' is not assignable to type 'Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. + Type '{ title: string; }[]' is not assignable to type '{ title: string; }[] & Promise<{ title: string; }[]>'. + Type '{ title: string; }[]' is missing the following properties from type 'Promise<{ title: string; }[]>': then, catch, finally, [Symbol.toStringTag] + + +==== tests/cases/compiler/reverseMappedConcretePropertiesInference.ts (1 errors) ==== + declare function test>(a: { + [K in keyof T]: { + foo: T[K]["foo"]; + onFoo: (fooArg: T[K]["foo"]) => void; + bar: T[K]["bar"]; + onBar: (barArg: T[K]["bar"]) => void; + }; + }): T; + + const res = test({ + a: { + foo: 'answer', + onFoo: (arg) => arg.length, + bar: 42, + onBar: (arg) => arg + 10 + }, + b: { + foo: true, + onFoo: (arg) => !!arg, + bar: [1, 2, 3], + onBar: (arg) => [arg, arg] + }, + }) + + interface QueryFunctionContext { + queryKey: TQueryKey; + } + + type QueryOptions = { + key: string; + fnData?: unknown; + }; + + type UseQueriesOptions> = { + [K in keyof T]: { + queryKey: T[K]["key"]; + queryFn?: ( + ctx: QueryFunctionContext + ) => Promise | T[K]["fnData"]; + }; + }; + + declare function useQueries>( + queries: [...UseQueriesOptions] + ): T; + + const resQueries = useQueries([ + { + queryKey: "users", + queryFn: (key) => [{ name: "Andarist" }], + }, + { + queryKey: "posts", + queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), + ~~~~~~~ +!!! error TS2322: Type '(key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>' is not assignable to type '(ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. +!!! error TS2322: Type 'Promise<{ title: string; }[]>' is not assignable to type '({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. +!!! error TS2322: Type 'Promise<{ title: string; }[]>' is not assignable to type 'Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. +!!! error TS2322: Type '{ title: string; }[]' is not assignable to type '{ title: string; }[] & Promise<{ title: string; }[]>'. +!!! error TS2322: Type '{ title: string; }[]' is missing the following properties from type 'Promise<{ title: string; }[]>': then, catch, finally, [Symbol.toStringTag] +!!! related TS6500 tests/cases/compiler/reverseMappedConcretePropertiesInference.ts:37:5: The expected type comes from property 'queryFn' which is declared here on type '{ queryKey: "posts"; queryFn?: ((ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>) | undefined; }' + } + ]); + \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types index 31cb77a54c78c..4e77f62651fd3 100644 --- a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types @@ -134,21 +134,21 @@ declare function useQueries>( ): T; const resQueries = useQueries([ ->resQueries : readonly QueryOptions[] ->useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : readonly QueryOptions[] +>resQueries : [{ key: "users"; }, { key: "posts"; }] +>useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : [{ key: "users"; }, { key: "posts"; }] >useQueries : (queries: [...UseQueriesOptions]) => T ->[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : ({ queryKey: string; queryFn: (key: QueryFunctionContext) => { name: string; }[]; } | { queryKey: string; queryFn: (key: QueryFunctionContext) => Promise<{ title: string; }[]>; })[] +>[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : [{ queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; }, { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; }] { ->{ queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], } : { queryKey: string; queryFn: (key: QueryFunctionContext) => { name: string; }[]; } +>{ queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], } : { queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; } queryKey: "users", ->queryKey : string +>queryKey : "users" >"users" : "users" queryFn: (key) => [{ name: "Andarist" }], ->queryFn : (key: QueryFunctionContext) => { name: string; }[] ->(key) => [{ name: "Andarist" }] : (key: QueryFunctionContext) => { name: string; }[] ->key : QueryFunctionContext +>queryFn : (key: QueryFunctionContext<"users">) => { name: string; }[] +>(key) => [{ name: "Andarist" }] : (key: QueryFunctionContext<"users">) => { name: string; }[] +>key : QueryFunctionContext<"users"> >[{ name: "Andarist" }] : { name: string; }[] >{ name: "Andarist" } : { name: string; } >name : string @@ -156,16 +156,16 @@ const resQueries = useQueries([ }, { ->{ queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), } : { queryKey: string; queryFn: (key: QueryFunctionContext) => Promise<{ title: string; }[]>; } +>{ queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), } : { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; } queryKey: "posts", ->queryKey : string +>queryKey : "posts" >"posts" : "posts" queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), ->queryFn : (key: QueryFunctionContext) => Promise<{ title: string; }[]> ->(key) => Promise.resolve([{ title: 'TS 5.1' }]) : (key: QueryFunctionContext) => Promise<{ title: string; }[]> ->key : QueryFunctionContext +>queryFn : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> +>(key) => Promise.resolve([{ title: 'TS 5.1' }]) : (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]> +>key : QueryFunctionContext<"posts"> >Promise.resolve([{ title: 'TS 5.1' }]) : Promise<{ title: string; }[]> >Promise.resolve : { (): Promise; (value: T): Promise>; (value: T | PromiseLike): Promise>; } >Promise : PromiseConstructor From 50e6a0c84a542addf9ce1d26f5934e37ac9750c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 18 Apr 2023 13:33:43 +0200 Subject: [PATCH 12/18] Avoid inferring an index under `InferencePriority.NakedTypeVariable` --- src/compiler/checker.ts | 2 +- ...ppedConcretePropertiesInference.errors.txt | 72 ------------------- ...rseMappedConcretePropertiesInference.types | 4 +- 3 files changed, 3 insertions(+), 75 deletions(-) delete mode 100644 tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9879c62f8eada..f273f83ed4879 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24511,7 +24511,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferFromTypes((source as IndexedAccessType).objectType, (target as IndexedAccessType).objectType); inferFromTypes((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType); } - else if (target.flags & TypeFlags.IndexedAccess) { + else if (!(priority & InferencePriority.NakedTypeVariable) && target.flags & TypeFlags.IndexedAccess) { const targetConstraint = (target as IndexedAccessType).objectType; const inference = getInferenceInfoForType(getActualTypeVariable(targetConstraint)); if (inference) { diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt b/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt deleted file mode 100644 index 513353a6e7e45..0000000000000 --- a/tests/baselines/reference/reverseMappedConcretePropertiesInference.errors.txt +++ /dev/null @@ -1,72 +0,0 @@ -tests/cases/compiler/reverseMappedConcretePropertiesInference.ts(54,5): error TS2322: Type '(key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>' is not assignable to type '(ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. - Type 'Promise<{ title: string; }[]>' is not assignable to type '({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. - Type 'Promise<{ title: string; }[]>' is not assignable to type 'Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. - Type '{ title: string; }[]' is not assignable to type '{ title: string; }[] & Promise<{ title: string; }[]>'. - Type '{ title: string; }[]' is missing the following properties from type 'Promise<{ title: string; }[]>': then, catch, finally, [Symbol.toStringTag] - - -==== tests/cases/compiler/reverseMappedConcretePropertiesInference.ts (1 errors) ==== - declare function test>(a: { - [K in keyof T]: { - foo: T[K]["foo"]; - onFoo: (fooArg: T[K]["foo"]) => void; - bar: T[K]["bar"]; - onBar: (barArg: T[K]["bar"]) => void; - }; - }): T; - - const res = test({ - a: { - foo: 'answer', - onFoo: (arg) => arg.length, - bar: 42, - onBar: (arg) => arg + 10 - }, - b: { - foo: true, - onFoo: (arg) => !!arg, - bar: [1, 2, 3], - onBar: (arg) => [arg, arg] - }, - }) - - interface QueryFunctionContext { - queryKey: TQueryKey; - } - - type QueryOptions = { - key: string; - fnData?: unknown; - }; - - type UseQueriesOptions> = { - [K in keyof T]: { - queryKey: T[K]["key"]; - queryFn?: ( - ctx: QueryFunctionContext - ) => Promise | T[K]["fnData"]; - }; - }; - - declare function useQueries>( - queries: [...UseQueriesOptions] - ): T; - - const resQueries = useQueries([ - { - queryKey: "users", - queryFn: (key) => [{ name: "Andarist" }], - }, - { - queryKey: "posts", - queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), - ~~~~~~~ -!!! error TS2322: Type '(key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>' is not assignable to type '(ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. -!!! error TS2322: Type 'Promise<{ title: string; }[]>' is not assignable to type '({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. -!!! error TS2322: Type 'Promise<{ title: string; }[]>' is not assignable to type 'Promise<{ title: string; }[] & Promise<{ title: string; }[]>>'. -!!! error TS2322: Type '{ title: string; }[]' is not assignable to type '{ title: string; }[] & Promise<{ title: string; }[]>'. -!!! error TS2322: Type '{ title: string; }[]' is missing the following properties from type 'Promise<{ title: string; }[]>': then, catch, finally, [Symbol.toStringTag] -!!! related TS6500 tests/cases/compiler/reverseMappedConcretePropertiesInference.ts:37:5: The expected type comes from property 'queryFn' which is declared here on type '{ queryKey: "posts"; queryFn?: ((ctx: QueryFunctionContext<"posts">) => ({ title: string; }[] & Promise<{ title: string; }[]>) | Promise<{ title: string; }[] & Promise<{ title: string; }[]>>) | undefined; }' - } - ]); - \ No newline at end of file diff --git a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types index 4e77f62651fd3..6c3c01a6e6e0a 100644 --- a/tests/baselines/reference/reverseMappedConcretePropertiesInference.types +++ b/tests/baselines/reference/reverseMappedConcretePropertiesInference.types @@ -134,8 +134,8 @@ declare function useQueries>( ): T; const resQueries = useQueries([ ->resQueries : [{ key: "users"; }, { key: "posts"; }] ->useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : [{ key: "users"; }, { key: "posts"; }] +>resQueries : [{ key: "users"; } & { fnData: { name: string; }[]; }, { key: "posts"; } & { fnData: { title: string; }[]; }] +>useQueries([ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }]) : [{ key: "users"; } & { fnData: { name: string; }[]; }, { key: "posts"; } & { fnData: { title: string; }[]; }] >useQueries : (queries: [...UseQueriesOptions]) => T >[ { queryKey: "users", queryFn: (key) => [{ name: "Andarist" }], }, { queryKey: "posts", queryFn: (key) => Promise.resolve([{ title: 'TS 5.1' }]), }] : [{ queryKey: "users"; queryFn: (key: QueryFunctionContext<"users">) => { name: string; }[]; }, { queryKey: "posts"; queryFn: (key: QueryFunctionContext<"posts">) => Promise<{ title: string; }[]>; }] { From 4febb9cab6b012f1d2681c23c5fa825ce7dad208 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 19 Apr 2023 10:48:37 +0200 Subject: [PATCH 13/18] always discard aggregate inference that is not assignable to the constraint --- src/compiler/checker.ts | 10 +++-- ...omIndicesNotAssignableToConstraint.symbols | 44 +++++++++++++++++++ ...FromIndicesNotAssignableToConstraint.types | 32 ++++++++++++++ ...ateFromIndicesNotAssignableToConstraint.ts | 18 ++++++++ 4 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.symbols create mode 100644 tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.types create mode 100644 tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f273f83ed4879..7ea7540b0837e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25164,9 +25164,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Build a candidate from all indexes let aggregateInference = getIntersectionType(inference.indexes); const constraint = getConstraintOfTypeParameter(signature.typeParameters![index]); - if (constraint) { - const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); - if (instantiatedConstraint.flags & TypeFlags.Union && !context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { + const instantiatedConstraint = constraint && instantiateType(constraint, context.nonFixingMapper); + if (!instantiatedConstraint || context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { + if (instantiatedConstraint && instantiatedConstraint.flags & TypeFlags.Union) { const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); if (discriminantProps) { let match: Type | undefined; @@ -25190,8 +25190,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } + else { + (inference.candidates || (inference.candidates = [])).push(aggregateInference); + } } - (inference.candidates || (inference.candidates = [])).push(aggregateInference); } const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined; if (inference.contraCandidates) { diff --git a/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.symbols b/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.symbols new file mode 100644 index 0000000000000..68a3357363d20 --- /dev/null +++ b/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.symbols @@ -0,0 +1,44 @@ +=== tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts === +interface Expression extends Node { +>Expression : Symbol(Expression, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 0, 0)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 89)) + + _expressionBrand: any; +>_expressionBrand : Symbol(Expression._expressionBrand, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 0, 35)) +} + +declare function setParent(child: T, parent: T["parent"] | undefined): T; +>setParent : Symbol(setParent, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 2, 1)) +>T : Symbol(T, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 27)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 89)) +>child : Symbol(child, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 43)) +>T : Symbol(T, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 27)) +>parent : Symbol(parent, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 52)) +>T : Symbol(T, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 27)) +>T : Symbol(T, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 27)) + +interface Node { +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 89)) + + readonly kind: number; +>kind : Symbol(Node.kind, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 6, 16)) + + readonly parent: Node; +>parent : Symbol(Node.parent, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 7, 26)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 89)) +} + +declare const expr: Expression +>expr : Symbol(expr, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 11, 13)) +>Expression : Symbol(Expression, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 0, 0)) + +declare const node: Node +>node : Symbol(node, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 12, 13)) +>Node : Symbol(Node, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --), Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 4, 89)) + +const res = setParent(expr, node) // Expression +>res : Symbol(res, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 14, 5)) +>setParent : Symbol(setParent, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 2, 1)) +>expr : Symbol(expr, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 11, 13)) +>node : Symbol(node, Decl(typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts, 12, 13)) + diff --git a/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.types b/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.types new file mode 100644 index 0000000000000..545c4e1b73cda --- /dev/null +++ b/tests/baselines/reference/typeInferenceAggregateFromIndicesNotAssignableToConstraint.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts === +interface Expression extends Node { + _expressionBrand: any; +>_expressionBrand : any +} + +declare function setParent(child: T, parent: T["parent"] | undefined): T; +>setParent : (child: T, parent: T["parent"] | undefined) => T +>child : T +>parent : T["parent"] | undefined + +interface Node { + readonly kind: number; +>kind : number + + readonly parent: Node; +>parent : Node +} + +declare const expr: Expression +>expr : Expression + +declare const node: Node +>node : Node + +const res = setParent(expr, node) // Expression +>res : Expression +>setParent(expr, node) : Expression +>setParent : (child: T, parent: T["parent"] | undefined) => T +>expr : Expression +>node : Node + diff --git a/tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts b/tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts new file mode 100644 index 0000000000000..0b3a82685c793 --- /dev/null +++ b/tests/cases/compiler/typeInferenceAggregateFromIndicesNotAssignableToConstraint.ts @@ -0,0 +1,18 @@ +// @strict: true +// @noEmit: true + +interface Expression extends Node { + _expressionBrand: any; +} + +declare function setParent(child: T, parent: T["parent"] | undefined): T; + +interface Node { + readonly kind: number; + readonly parent: Node; +} + +declare const expr: Expression +declare const node: Node + +const res = setParent(expr, node) // Expression \ No newline at end of file From c2d4b0f80be9fd03f0475d15fa9278739bfc0943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 19 Apr 2023 11:27:27 +0200 Subject: [PATCH 14/18] bring back the union discriminating logic --- src/compiler/checker.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7ea7540b0837e..dd93ad0b3ecce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25163,10 +25163,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (inference.indexes) { // Build a candidate from all indexes let aggregateInference = getIntersectionType(inference.indexes); - const constraint = getConstraintOfTypeParameter(signature.typeParameters![index]); - const instantiatedConstraint = constraint && instantiateType(constraint, context.nonFixingMapper); - if (!instantiatedConstraint || context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { - if (instantiatedConstraint && instantiatedConstraint.flags & TypeFlags.Union) { + const constraint = getConstraintOfTypeParameter(inference.typeParameter); + if (constraint) { + const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); + const comparableToConstraint = context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference)); + if (instantiatedConstraint.flags & TypeFlags.Union && !comparableToConstraint) { + const originalAggregateInference = aggregateInference; const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); if (discriminantProps) { let match: Type | undefined; @@ -25189,11 +25191,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { aggregateInference = getSpreadType(match, aggregateInference, /*symbol*/ undefined, /*propegatedFlags*/ 0, /*readonly*/ false); } } + if (originalAggregateInference !== aggregateInference) { + inference.candidates = append(inference.candidates, aggregateInference); + } } - else { - (inference.candidates || (inference.candidates = [])).push(aggregateInference); + else if (comparableToConstraint) { + inference.candidates = append(inference.candidates, aggregateInference); } } + else { + inference.candidates = append(inference.candidates, aggregateInference); + } } const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined; if (inference.contraCandidates) { From 11b02d0b3e3d66944b40e9022afedf0216901bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 19 Apr 2023 11:42:10 +0200 Subject: [PATCH 15/18] fixed incorrect indexed access in tests --- .../indexAccessCombinedInference.errors.txt | 130 ------------------ .../reference/indexAccessCombinedInference.js | 4 +- .../indexAccessCombinedInference.symbols | 10 +- .../indexAccessCombinedInference.types | 18 +-- .../compiler/indexAccessCombinedInference.ts | 4 +- 5 files changed, 18 insertions(+), 148 deletions(-) delete mode 100644 tests/baselines/reference/indexAccessCombinedInference.errors.txt diff --git a/tests/baselines/reference/indexAccessCombinedInference.errors.txt b/tests/baselines/reference/indexAccessCombinedInference.errors.txt deleted file mode 100644 index 56bd3f9e28e35..0000000000000 --- a/tests/baselines/reference/indexAccessCombinedInference.errors.txt +++ /dev/null @@ -1,130 +0,0 @@ -tests/cases/compiler/indexAccessCombinedInference.ts(104,21): error TS2536: Type '"0"' cannot be used to index type 'this'. -tests/cases/compiler/indexAccessCombinedInference.ts(106,49): error TS2536: Type '"0"' cannot be used to index type 'T'. -tests/cases/compiler/indexAccessCombinedInference.ts(106,60): error TS2536: Type '"0"' cannot be used to index type 'T'. -tests/cases/compiler/indexAccessCombinedInference.ts(106,77): error TS2536: Type '"1"' cannot be used to index type 'T'. -tests/cases/compiler/indexAccessCombinedInference.ts(106,86): error TS2536: Type '"0"' cannot be used to index type 'T'. -tests/cases/compiler/indexAccessCombinedInference.ts(106,110): error TS2536: Type '"1"' cannot be used to index type 'T'. - - -==== tests/cases/compiler/indexAccessCombinedInference.ts (6 errors) ==== - // Simple case - interface Args { - TA: object, - TY: object - } - - declare function foo( - a: T["TA"], - b: T["TY"]): T["TA"] & T["TY"]; - - const x = foo({ - x: { - j: 12, - i: 11 - } - }, { y: 42 }); - - // Union result type - interface A { - foo: number; - } - interface B { - bar: string; - } - declare const something: A | B; - - const y = foo(something, { bat: 42 }); - - // Union key type - interface Args2 { - TA?: object, // Optional since only one of TA or TB needs to be infered in the below argument list - TB?: object, - TY: object - } - declare function foo2( - a: T["TA"] | T["TB"], - b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; - declare function foo3( // Morally equivalent to foo2 - a: T["TA" | "TB"], - b: T["TY"]): {a: T["TA"], b: T["TB"]} & T["TY"]; - let z = foo2({ - x: { - j: 12, - i: 11 - } - }, { y: 42 }); - let zz = foo3({ - x: { - j: 12, - i: 11 - } - }, { y: 42 }); - z = zz; - zz = z; - - // Higher-order - interface Args3 { - Key: "A" | "B", - A: object, - B: object, - Merge: object, - } - declare const either: "A" | "B"; - declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"]): T[T["Key"]] & T["Merge"]; - - const opt1 = pickOne("A", {x: 12}, {y: ""}, {z: /./}); - const opt2 = pickOne("B", {x: 12}, {y: ""}, {z: /./}); - const opt3 = pickOne(either, {x: 12}, {y: ""}, {z: /./}); - - const pickDelayed = (x: TKey) => pickOne(x, {j: x}, {i: x}, {chosen: x}); - const opt4 = pickDelayed("A"); - const opt5 = pickDelayed("B"); - const opt6 = pickDelayed(either); - - // Reopenable - interface Args3 { - /** - * One must make patched parameters optional, otherwise signatures expecting the unpatched - * interface (ie, pickOne above) will not be able to produce a type satisfying the interface - * (as there are no inference sites for the new members) and will fall back to the constraints on each member - */ - Extra?: object, - } - declare function pickOne(key: T["Key"], left: T["A"], right: T["B"], into: T["Merge"], extra: T["Extra"]): T[T["Key"]] & {into: T["Merge"], extra: T["Extra"]}; - const opt7 = pickOne("A", {x: 12}, {y: ""}, {z: /./}, {z: /./}); - const opt8 = pickOne("B", {x: 12}, {y: ""}, {z: /./}, {z: /./}); - const opt9 = pickOne(either, {x: 12}, {y: ""}, {z: /./}, {z: /./}); - - // Interactions with `this` types - interface TPicker { - Key: keyof this, - X: number, - Y: string - } - declare function chooseLiteral(choice: T["Key"], x: T["X"], y:T["Y"]): T[T["Key"]]; - const cx = chooseLiteral("X", 1, "no"); - const cy = chooseLiteral("Y", 0, "yes"); - const ceither = chooseLiteral("X" as "X" | "Y", 1, "yes"); - const cneither = chooseLiteral("Key", 0, "no"); - - // Multiple inference sites - interface Args4 { - 0: object, - 1: Record, - ~~~~~~~~~ -!!! error TS2536: Type '"0"' cannot be used to index type 'this'. - } - declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; - ~~~~~~ -!!! error TS2536: Type '"0"' cannot be used to index type 'T'. - ~~~~~~ -!!! error TS2536: Type '"0"' cannot be used to index type 'T'. - ~~~~~~ -!!! error TS2536: Type '"1"' cannot be used to index type 'T'. - ~~~~~~ -!!! error TS2536: Type '"0"' cannot be used to index type 'T'. - ~~~~~~ -!!! error TS2536: Type '"1"' cannot be used to index type 'T'. - - const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); - \ No newline at end of file diff --git a/tests/baselines/reference/indexAccessCombinedInference.js b/tests/baselines/reference/indexAccessCombinedInference.js index b31a76ef75725..12232d7a312a8 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.js +++ b/tests/baselines/reference/indexAccessCombinedInference.js @@ -102,9 +102,9 @@ const cneither = chooseLiteral("Key", 0, "no"); // Multiple inference sites interface Args4 { 0: object, - 1: Record, + 1: Record, } -declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; +declare function dualInputs(x: T[0], y: T[0], toDelay: T[1]): T[0] & {transformers: T[1]}; const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); diff --git a/tests/baselines/reference/indexAccessCombinedInference.symbols b/tests/baselines/reference/indexAccessCombinedInference.symbols index 599b8f74394da..e0e823bd60977 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.symbols +++ b/tests/baselines/reference/indexAccessCombinedInference.symbols @@ -347,23 +347,23 @@ interface Args4 { 0: object, >0 : Symbol(Args4[0], Decl(indexAccessCombinedInference.ts, 101, 17)) - 1: Record, + 1: Record, >1 : Symbol(Args4[1], Decl(indexAccessCombinedInference.ts, 102, 14)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) >Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) } -declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; +declare function dualInputs(x: T[0], y: T[0], toDelay: T[1]): T[0] & {transformers: T[1]}; >dualInputs : Symbol(dualInputs, Decl(indexAccessCombinedInference.ts, 104, 1)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) >Args4 : Symbol(Args4, Decl(indexAccessCombinedInference.ts, 98, 47)) >x : Symbol(x, Decl(indexAccessCombinedInference.ts, 105, 45)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) ->y : Symbol(y, Decl(indexAccessCombinedInference.ts, 105, 55)) +>y : Symbol(y, Decl(indexAccessCombinedInference.ts, 105, 53)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) ->toDelay : Symbol(toDelay, Decl(indexAccessCombinedInference.ts, 105, 66)) +>toDelay : Symbol(toDelay, Decl(indexAccessCombinedInference.ts, 105, 62)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) ->transformers : Symbol(transformers, Decl(indexAccessCombinedInference.ts, 105, 95)) +>transformers : Symbol(transformers, Decl(indexAccessCombinedInference.ts, 105, 87)) >T : Symbol(T, Decl(indexAccessCombinedInference.ts, 105, 28)) const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); diff --git a/tests/baselines/reference/indexAccessCombinedInference.types b/tests/baselines/reference/indexAccessCombinedInference.types index 523749d82c9ac..491bf3844d93d 100644 --- a/tests/baselines/reference/indexAccessCombinedInference.types +++ b/tests/baselines/reference/indexAccessCombinedInference.types @@ -383,20 +383,20 @@ interface Args4 { 0: object, >0 : object - 1: Record, ->1 : Record + 1: Record, +>1 : Record } -declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; ->dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"];} ->x : T["0"] ->y : T["0"] ->toDelay : T["1"] ->transformers : T["1"] +declare function dualInputs(x: T[0], y: T[0], toDelay: T[1]): T[0] & {transformers: T[1]}; +>dualInputs : (x: T[0], y: T[0], toDelay: T[1]) => T[0] & { transformers: T[1];} +>x : T[0] +>y : T[0] +>toDelay : T[1] +>transformers : T[1] const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); >result : { x: number; } & { x: number; } & { transformers: { x: () => ""; }; } >dualInputs({x: 0}, {x: 1}, {x: () => ""}) : { x: number; } & { x: number; } & { transformers: { x: () => ""; }; } ->dualInputs : (x: T["0"], y: T["0"], toDelay: T["1"]) => T["0"] & { transformers: T["1"]; } +>dualInputs : (x: T[0], y: T[0], toDelay: T[1]) => T[0] & { transformers: T[1]; } >{x: 0} : { x: number; } >x : number >0 : 0 diff --git a/tests/cases/compiler/indexAccessCombinedInference.ts b/tests/cases/compiler/indexAccessCombinedInference.ts index 59af2dd60fa1f..e72131d320f04 100644 --- a/tests/cases/compiler/indexAccessCombinedInference.ts +++ b/tests/cases/compiler/indexAccessCombinedInference.ts @@ -101,8 +101,8 @@ const cneither = chooseLiteral("Key", 0, "no"); // Multiple inference sites interface Args4 { 0: object, - 1: Record, + 1: Record, } -declare function dualInputs(x: T["0"], y: T["0"], toDelay: T["1"]): T["0"] & {transformers: T["1"]}; +declare function dualInputs(x: T[0], y: T[0], toDelay: T[1]): T[0] & {transformers: T[1]}; const result = dualInputs({x: 0}, {x: 1}, {x: () => ""}); From 97adac5dd85cefbe817860fb2763244f774f1198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 19 Apr 2023 13:45:37 +0200 Subject: [PATCH 16/18] only use the aggregate inference when there are no other candidates --- src/compiler/checker.ts | 49 ++-- .../reference/keyofAndIndexedAccess.types | 6 +- ...actDefaultPropsInferenceSuccess.errors.txt | 51 +++- .../thisTypeInObjectLiterals2.errors.txt | 260 ------------------ .../reference/thisTypeInObjectLiterals2.js | 10 +- .../thisTypeInObjectLiterals2.symbols | 132 +++++---- .../reference/thisTypeInObjectLiterals2.types | 36 ++- .../thisType/thisTypeInObjectLiterals2.ts | 2 - 8 files changed, 158 insertions(+), 388 deletions(-) delete mode 100644 tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dd93ad0b3ecce..c5d84fbfcd2cd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24522,7 +24522,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inferenceType = inferenceTypeSymbol && getDeclaredTypeOfSymbol(inferenceTypeSymbol); if (inferenceType && inferenceType !== unknownType) { const mapper = createTypeMapper(getSymbolLinks(inferenceTypeSymbol).typeParameters!, [source, (target as IndexedAccessType).indexType]); - (inference.indexes || (inference.indexes = [])).push(instantiateType(inferenceType, mapper)); + inference.indexes = append(inference.indexes, instantiateType(inferenceType, mapper)); } } return; @@ -25160,15 +25160,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let inferredType: Type | undefined; const signature = context.signature; if (signature) { - if (inference.indexes) { - // Build a candidate from all indexes + const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined; + if (inference.contraCandidates) { + // If we have both co- and contra-variant inferences, we use the co-variant inference if it is not 'never', + // it is a subtype of some contra-variant inference, and no other type parameter is constrained to this type + // parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference. + const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) && + some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) && + every(context.inferences, other => + other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter || + every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType))); + inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference); + } + else if (inferredCovariantType) { + inferredType = inferredCovariantType; + } + else if (inference.indexes) { let aggregateInference = getIntersectionType(inference.indexes); const constraint = getConstraintOfTypeParameter(inference.typeParameter); if (constraint) { const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper); - const comparableToConstraint = context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference)); - if (instantiatedConstraint.flags & TypeFlags.Union && !comparableToConstraint) { - const originalAggregateInference = aggregateInference; + if (instantiatedConstraint.flags & TypeFlags.Union && !context.compareTypes(aggregateInference, getTypeWithThisArgument(instantiatedConstraint, aggregateInference))) { const discriminantProps = findDiscriminantProperties(getPropertiesOfType(aggregateInference), instantiatedConstraint); if (discriminantProps) { let match: Type | undefined; @@ -25191,32 +25203,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { aggregateInference = getSpreadType(match, aggregateInference, /*symbol*/ undefined, /*propegatedFlags*/ 0, /*readonly*/ false); } } - if (originalAggregateInference !== aggregateInference) { - inference.candidates = append(inference.candidates, aggregateInference); - } - } - else if (comparableToConstraint) { - inference.candidates = append(inference.candidates, aggregateInference); } } - else { - inference.candidates = append(inference.candidates, aggregateInference); - } - } - const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined; - if (inference.contraCandidates) { - // If we have both co- and contra-variant inferences, we use the co-variant inference if it is not 'never', - // it is a subtype of some contra-variant inference, and no other type parameter is constrained to this type - // parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference. - const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) && - some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) && - every(context.inferences, other => - other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter || - every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType))); - inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference); - } - else if (inferredCovariantType) { - inferredType = inferredCovariantType; + inferredType = aggregateInference; } else if (context.flags & InferenceFlags.NoDefault) { // We use silentNeverType as the wildcard that signals no inferences. diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 5a6e943ed2d2a..e8d59f2fa7978 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -58,10 +58,10 @@ type K00 = keyof any; // string >K00 : string | number | symbol type K01 = keyof string; // "toString" | "charAt" | ... ->K01 : number | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "length" | "substr" | "toString" | "valueOf" +>K01 : number | "length" | "toString" | "concat" | "slice" | "indexOf" | "lastIndexOf" | "charAt" | "charCodeAt" | "localeCompare" | "match" | "replace" | "search" | "split" | "substring" | "toLowerCase" | "toLocaleLowerCase" | "toUpperCase" | "toLocaleUpperCase" | "trim" | "substr" | "valueOf" type K02 = keyof number; // "toString" | "toFixed" | "toExponential" | ... ->K02 : "toFixed" | "toExponential" | "toPrecision" | "toString" | "toLocaleString" | "valueOf" +>K02 : "toString" | "toLocaleString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" type K03 = keyof boolean; // "valueOf" >K03 : "valueOf" @@ -97,7 +97,7 @@ type K14 = keyof Object; // "constructor" | "toString" | ... >K14 : keyof Object type K15 = keyof E; // "toString" | "toFixed" | "toExponential" | ... ->K15 : "toFixed" | "toExponential" | "toPrecision" | "toString" | "toLocaleString" | "valueOf" +>K15 : "toString" | "toLocaleString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" type K16 = keyof [string, number]; // "0" | "1" | "length" | "toString" | ... >K16 : keyof [string, number] diff --git a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt index 3c3962fa6ae8d..26bf596fa531b 100644 --- a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt +++ b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt @@ -1,10 +1,30 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(24,42): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,36): error TS2769: No overload matches this call. + Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. + Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. + Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. + Type 'void' is not assignable to type 'boolean'. + Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. + Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,42): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(43,41): error TS2769: No overload matches this call. + Overload 1 of 2, '(props: Readonly): FieldFeedbackBeta', gave the following error. + Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. + Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. + Type 'void' is not assignable to type 'boolean'. + Overload 2 of 2, '(props: Props, context?: any): FieldFeedbackBeta', gave the following error. + Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(61,43): error TS7006: Parameter 'value' implicitly has an 'any' type. +tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: No overload matches this call. + Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. + Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. + Type 'void' is not assignable to type 'boolean'. + Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. + Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: Parameter 'value' implicitly has an 'any' type. -==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (4 errors) ==== +==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (7 errors) ==== /// import React from 'react'; @@ -34,6 +54,16 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: // Error: Void not assignable to boolean const Test2 = () => console.log(value)} />; + ~~~~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. +!!! error TS2769: Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type 'void' is not assignable to type 'boolean'. +!!! error TS2769: Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. +!!! error TS2769: Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' ~~~~~ !!! error TS7006: Parameter 'value' implicitly has an 'any' type. @@ -52,6 +82,16 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: // Error: Void not assignable to boolean const Test2a = () => console.log(value)} error>Hah; + ~~~~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedbackBeta', gave the following error. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type 'void' is not assignable to type 'boolean'. +!!! error TS2769: Overload 2 of 2, '(props: Props, context?: any): FieldFeedbackBeta', gave the following error. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children"> & Partial & Readonly, keyof Props>> & Partial>' +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children"> & Partial & Readonly, keyof Props>> & Partial>' interface MyPropsProps extends Props { when: (value: string) => boolean; @@ -75,6 +115,15 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: // Error: Void not assignable to boolean const Test4 = () => console.log(value)} />; + ~~~~ +!!! error TS2769: No overload matches this call. +!!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. +!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type 'void' is not assignable to type 'boolean'. +!!! error TS2769: Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. +!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' +!!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' ~~~~~ !!! error TS7006: Parameter 'value' implicitly has an 'any' type. diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt b/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt deleted file mode 100644 index b00cc7e423fb5..0000000000000 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.errors.txt +++ /dev/null @@ -1,260 +0,0 @@ -tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(224,9): error TS2322: Type '{ test(): number; hello: { get(): string; set(value: string): void; }; }' is not assignable to type 'Accessors<{ hello: string; }>'. - Object literal may only specify known properties, and 'test' does not exist in type 'Accessors<{ hello: string; }>'. -tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(239,5): error TS2339: Property 'f' does not exist on type '{ x: number; y: number; }'. -tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(240,5): error TS2339: Property 'test' does not exist on type '{ x: number; y: number; }'. -tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts(241,5): error TS2339: Property 'hello' does not exist on type '{ x: number; y: number; }'. - - -==== tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts (4 errors) ==== - // In methods of an object literal with no contextual type, 'this' has the type - // of the object literal. - - let obj1 = { - a: 1, - f() { - return this.a; - }, - b: "hello", - c: { - g() { - this.g(); - } - }, - get d() { - return this.a; - }, - get e() { - return this.b; - }, - set e(value) { - this.b = value; - } - }; - - // In methods of an object literal with a contextual type, 'this' has the - // contextual type. - - type Point = { - x: number; - y: number; - z?: number; - moveBy(dx: number, dy: number, dz?: number): void; - } - - let p1: Point = { - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }; - - let p2: Point | null = { - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }; - - let p3: Point | undefined = { - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }; - - let p4: Point | null | undefined = { - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }; - - declare function f1(p: Point): void; - - f1({ - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }); - - declare function f2(p: Point | null | undefined): void; - - f2({ - x: 10, - y: 20, - moveBy(dx, dy, dz) { - this.x += dx; - this.y += dy; - if (this.z && dz) { - this.z += dz; - } - } - }); - - // In methods of an object literal with a contextual type that includes some - // ThisType, 'this' is of type T. - - type ObjectDescriptor = { - data?: D; - methods?: M & ThisType; // Type of 'this' in methods is D & M - } - - declare function makeObject(desc: ObjectDescriptor): D & M; - - let x1 = makeObject({ - data: { x: 0, y: 0 }, - methods: { - moveBy(dx: number, dy: number) { - this.x += dx; // Strongly typed this - this.y += dy; // Strongly typed this - } - } - }); - - // In methods contained in an object literal with a contextual type that includes - // some ThisType, 'this' is of type T. - - type ObjectDescriptor2 = ThisType & { - data?: D; - methods?: M; - } - - declare function makeObject2(desc: ObjectDescriptor): D & M; - - let x2 = makeObject2({ - data: { x: 0, y: 0 }, - methods: { - moveBy(dx: number, dy: number) { - this.x += dx; // Strongly typed this - this.y += dy; // Strongly typed this - } - } - }); - - // Check pattern similar to Object.defineProperty and Object.defineProperties - - type PropDesc = { - value?: T; - get?(): T; - set?(value: T): void; - } - - type PropDescMap = { - [K in keyof T]: PropDesc; - } - - declare function defineProp(obj: T, name: K, desc: PropDesc & ThisType): T & Record; - - declare function defineProps(obj: T, descs: PropDescMap & ThisType): T & U; - - let p10 = defineProp(p1, "foo", { value: 42 }); - p10.foo = p10.foo + 1; - - let p11 = defineProp(p1, "bar", { - get() { - return this.x; - }, - set(value: number) { - this.x = value; - } - }); - p11.bar = p11.bar + 1; - - let p12 = defineProps(p1, { - foo: { - value: 42 - }, - bar: { - get(): number { - return this.x; - }, - set(value: number) { - this.x = value; - } - } - }); - p12.foo = p12.foo + 1; - p12.bar = p12.bar + 1; - - // Proof of concept for typing of Vue.js - - type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; - - type Dictionary = { [x: string]: T } - - type Computed = { - get?(): T; - set?(value: T): void; - } - - type VueOptions = ThisType & { - data?: D | (() => D); - methods?: M; - computed?: Accessors

; - } - - declare const Vue: new (options: VueOptions) => D & M & P; - - let vue = new Vue({ - data: () => ({ x: 1, y: 2 }), - methods: { - f(x: string) { - return this.x; - } - }, - computed: { - test(): number { - ~~~~ -!!! error TS2322: Type '{ test(): number; hello: { get(): string; set(value: string): void; }; }' is not assignable to type 'Accessors<{ hello: string; }>'. -!!! error TS2322: Object literal may only specify known properties, and 'test' does not exist in type 'Accessors<{ hello: string; }>'. -!!! related TS6500 tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts:211:5: The expected type comes from property 'computed' which is declared here on type 'VueOptions<{ x: number; y: number; }, { f(x: string): number; }, { hello: string; }>' - return this.x; - }, - hello: { - get() { - return "hi"; - }, - set(value: string) { - } - } - } - }); - - vue; - vue.x; - vue.f("abc"); - ~ -!!! error TS2339: Property 'f' does not exist on type '{ x: number; y: number; }'. - vue.test; - ~~~~ -!!! error TS2339: Property 'test' does not exist on type '{ x: number; y: number; }'. - vue.hello; - ~~~~~ -!!! error TS2339: Property 'hello' does not exist on type '{ x: number; y: number; }'. - \ No newline at end of file diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.js b/tests/baselines/reference/thisTypeInObjectLiterals2.js index 1114422c9c0ee..10699916b9d54 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.js +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.js @@ -199,8 +199,6 @@ p12.bar = p12.bar + 1; type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; -type Dictionary = { [x: string]: T } - type Computed = { get?(): T; set?(value: T): void; @@ -468,9 +466,6 @@ declare let p12: Point & { type Accessors = { [K in keyof T]: (() => T[K]) | Computed; }; -type Dictionary = { - [x: string]: T; -}; type Computed = { get?(): T; set?(value: T): void; @@ -484,4 +479,9 @@ declare const Vue: new (options: VueOptions) => D & M & P; declare let vue: { x: number; y: number; +} & { + f(x: string): number; +} & { + test: number; + hello: string; }; diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.symbols b/tests/baselines/reference/thisTypeInObjectLiterals2.symbols index 313a9a619fa30..02e15ad8ba1ec 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.symbols +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.symbols @@ -645,133 +645,131 @@ type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; >T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 198, 15)) >T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 198, 15)) >K : Symbol(K, Decl(thisTypeInObjectLiterals2.ts, 198, 23)) ->Computed : Symbol(Computed, Decl(thisTypeInObjectLiterals2.ts, 200, 39)) +>Computed : Symbol(Computed, Decl(thisTypeInObjectLiterals2.ts, 198, 70)) >T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 198, 15)) >K : Symbol(K, Decl(thisTypeInObjectLiterals2.ts, 198, 23)) -type Dictionary = { [x: string]: T } ->Dictionary : Symbol(Dictionary, Decl(thisTypeInObjectLiterals2.ts, 198, 70)) ->T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 200, 16)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 200, 24)) ->T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 200, 16)) - type Computed = { ->Computed : Symbol(Computed, Decl(thisTypeInObjectLiterals2.ts, 200, 39)) ->T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 202, 14)) +>Computed : Symbol(Computed, Decl(thisTypeInObjectLiterals2.ts, 198, 70)) +>T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 200, 14)) get?(): T; ->get : Symbol(get, Decl(thisTypeInObjectLiterals2.ts, 202, 20)) ->T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 202, 14)) +>get : Symbol(get, Decl(thisTypeInObjectLiterals2.ts, 200, 20)) +>T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 200, 14)) set?(value: T): void; ->set : Symbol(set, Decl(thisTypeInObjectLiterals2.ts, 203, 14)) ->value : Symbol(value, Decl(thisTypeInObjectLiterals2.ts, 204, 9)) ->T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 202, 14)) +>set : Symbol(set, Decl(thisTypeInObjectLiterals2.ts, 201, 14)) +>value : Symbol(value, Decl(thisTypeInObjectLiterals2.ts, 202, 9)) +>T : Symbol(T, Decl(thisTypeInObjectLiterals2.ts, 200, 14)) } type VueOptions = ThisType & { ->VueOptions : Symbol(VueOptions, Decl(thisTypeInObjectLiterals2.ts, 205, 1)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 207, 16)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 207, 18)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 207, 21)) +>VueOptions : Symbol(VueOptions, Decl(thisTypeInObjectLiterals2.ts, 203, 1)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 205, 16)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 205, 18)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 205, 21)) >ThisType : Symbol(ThisType, Decl(lib.es5.d.ts, --, --)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 207, 16)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 207, 18)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 207, 21)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 205, 16)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 205, 18)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 205, 21)) data?: D | (() => D); ->data : Symbol(data, Decl(thisTypeInObjectLiterals2.ts, 207, 50)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 207, 16)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 207, 16)) +>data : Symbol(data, Decl(thisTypeInObjectLiterals2.ts, 205, 50)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 205, 16)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 205, 16)) methods?: M; ->methods : Symbol(methods, Decl(thisTypeInObjectLiterals2.ts, 208, 25)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 207, 18)) +>methods : Symbol(methods, Decl(thisTypeInObjectLiterals2.ts, 206, 25)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 205, 18)) computed?: Accessors

; ->computed : Symbol(computed, Decl(thisTypeInObjectLiterals2.ts, 209, 16)) +>computed : Symbol(computed, Decl(thisTypeInObjectLiterals2.ts, 207, 16)) >Accessors : Symbol(Accessors, Decl(thisTypeInObjectLiterals2.ts, 194, 22)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 207, 21)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 205, 21)) } declare const Vue: new (options: VueOptions) => D & M & P; ->Vue : Symbol(Vue, Decl(thisTypeInObjectLiterals2.ts, 213, 13)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 213, 24)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 213, 26)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 213, 29)) ->options : Symbol(options, Decl(thisTypeInObjectLiterals2.ts, 213, 33)) ->VueOptions : Symbol(VueOptions, Decl(thisTypeInObjectLiterals2.ts, 205, 1)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 213, 24)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 213, 26)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 213, 29)) ->D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 213, 24)) ->M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 213, 26)) ->P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 213, 29)) +>Vue : Symbol(Vue, Decl(thisTypeInObjectLiterals2.ts, 211, 13)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 211, 24)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 211, 26)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 211, 29)) +>options : Symbol(options, Decl(thisTypeInObjectLiterals2.ts, 211, 33)) +>VueOptions : Symbol(VueOptions, Decl(thisTypeInObjectLiterals2.ts, 203, 1)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 211, 24)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 211, 26)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 211, 29)) +>D : Symbol(D, Decl(thisTypeInObjectLiterals2.ts, 211, 24)) +>M : Symbol(M, Decl(thisTypeInObjectLiterals2.ts, 211, 26)) +>P : Symbol(P, Decl(thisTypeInObjectLiterals2.ts, 211, 29)) let vue = new Vue({ ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) ->Vue : Symbol(Vue, Decl(thisTypeInObjectLiterals2.ts, 213, 13)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) +>Vue : Symbol(Vue, Decl(thisTypeInObjectLiterals2.ts, 211, 13)) data: () => ({ x: 1, y: 2 }), ->data : Symbol(data, Decl(thisTypeInObjectLiterals2.ts, 215, 19)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) ->y : Symbol(y, Decl(thisTypeInObjectLiterals2.ts, 216, 24)) +>data : Symbol(data, Decl(thisTypeInObjectLiterals2.ts, 213, 19)) +>x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) +>y : Symbol(y, Decl(thisTypeInObjectLiterals2.ts, 214, 24)) methods: { ->methods : Symbol(methods, Decl(thisTypeInObjectLiterals2.ts, 216, 33)) +>methods : Symbol(methods, Decl(thisTypeInObjectLiterals2.ts, 214, 33)) f(x: string) { ->f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 217, 14)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 218, 10)) +>f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 215, 14)) +>x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 10)) return this.x; ->this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) ->this : Symbol(__object, Decl(thisTypeInObjectLiterals2.ts, 216, 17)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) +>this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) +>x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) } }, computed: { ->computed : Symbol(computed, Decl(thisTypeInObjectLiterals2.ts, 221, 6)) +>computed : Symbol(computed, Decl(thisTypeInObjectLiterals2.ts, 219, 6)) test(): number { ->test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 222, 15)) +>test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 220, 15)) return this.x; ->this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) ->this : Symbol(__object, Decl(thisTypeInObjectLiterals2.ts, 216, 17)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) +>this.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) +>x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) }, hello: { ->hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 225, 10)) +>hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 223, 10)) get() { ->get : Symbol(get, Decl(thisTypeInObjectLiterals2.ts, 226, 16)) +>get : Symbol(get, Decl(thisTypeInObjectLiterals2.ts, 224, 16)) return "hi"; }, set(value: string) { ->set : Symbol(set, Decl(thisTypeInObjectLiterals2.ts, 229, 14)) ->value : Symbol(value, Decl(thisTypeInObjectLiterals2.ts, 230, 16)) +>set : Symbol(set, Decl(thisTypeInObjectLiterals2.ts, 227, 14)) +>value : Symbol(value, Decl(thisTypeInObjectLiterals2.ts, 228, 16)) } } } }); vue; ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) vue.x; ->vue.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) ->x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 216, 18)) +>vue.x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) +>x : Symbol(x, Decl(thisTypeInObjectLiterals2.ts, 214, 18)) vue.f("abc"); ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) +>vue.f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 215, 14)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) +>f : Symbol(f, Decl(thisTypeInObjectLiterals2.ts, 215, 14)) vue.test; ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) +>vue.test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 220, 15)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) +>test : Symbol(test, Decl(thisTypeInObjectLiterals2.ts, 220, 15)) vue.hello; ->vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 215, 3)) +>vue.hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 223, 10)) +>vue : Symbol(vue, Decl(thisTypeInObjectLiterals2.ts, 213, 3)) +>hello : Symbol(hello, Decl(thisTypeInObjectLiterals2.ts, 223, 10)) diff --git a/tests/baselines/reference/thisTypeInObjectLiterals2.types b/tests/baselines/reference/thisTypeInObjectLiterals2.types index 1d00535f1fc89..cc13ce2cb6e2a 100644 --- a/tests/baselines/reference/thisTypeInObjectLiterals2.types +++ b/tests/baselines/reference/thisTypeInObjectLiterals2.types @@ -671,10 +671,6 @@ p12.bar = p12.bar + 1; type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; >Accessors : Accessors -type Dictionary = { [x: string]: T } ->Dictionary : Dictionary ->x : string - type Computed = { >Computed : Computed @@ -704,8 +700,8 @@ declare const Vue: new (options: VueOptions) => D & M & P; >options : VueOptions let vue = new Vue({ ->vue : { x: number; y: number; } ->new Vue({ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }}) : { x: number; y: number; } +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>new Vue({ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }}) : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } >Vue : new (options: VueOptions) => D & M & P >{ data: () => ({ x: 1, y: 2 }), methods: { f(x: string) { return this.x; } }, computed: { test(): number { return this.x; }, hello: { get() { return "hi"; }, set(value: string) { } } }} : { data: () => { x: number; y: number; }; methods: { f(x: string): number; }; computed: { test(): number; hello: { get(): string; set(value: string): void; }; }; } @@ -729,7 +725,7 @@ let vue = new Vue({ return this.x; >this.x : number ->this : { x: number; y: number; } +>this : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } >x : number } }, @@ -742,7 +738,7 @@ let vue = new Vue({ return this.x; >this.x : number ->this : { x: number; y: number; } +>this : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } >x : number }, @@ -766,27 +762,27 @@ let vue = new Vue({ }); vue; ->vue : { x: number; y: number; } +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } vue.x; >vue.x : number ->vue : { x: number; y: number; } +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } >x : number vue.f("abc"); ->vue.f("abc") : any ->vue.f : any ->vue : { x: number; y: number; } ->f : any +>vue.f("abc") : number +>vue.f : (x: string) => number +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>f : (x: string) => number >"abc" : "abc" vue.test; ->vue.test : any ->vue : { x: number; y: number; } ->test : any +>vue.test : number +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>test : number vue.hello; ->vue.hello : any ->vue : { x: number; y: number; } ->hello : any +>vue.hello : string +>vue : { x: number; y: number; } & { f(x: string): number; } & { test: number; hello: string; } +>hello : string diff --git a/tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts b/tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts index 27861f81da6a6..3a47f85fe4cca 100644 --- a/tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts +++ b/tests/cases/conformance/types/thisType/thisTypeInObjectLiterals2.ts @@ -202,8 +202,6 @@ p12.bar = p12.bar + 1; type Accessors = { [K in keyof T]: (() => T[K]) | Computed }; -type Dictionary = { [x: string]: T } - type Computed = { get?(): T; set?(value: T): void; From 8ffcb944f594bd1fd852f14312f046eae8aeba63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 20 Apr 2023 20:23:33 +0200 Subject: [PATCH 17/18] do not collect index-based inferences from non-inferrable types --- src/compiler/checker.ts | 10 ++++-- ...actDefaultPropsInferenceSuccess.errors.txt | 34 ++++++------------- .../reactDefaultPropsInferenceSuccess.types | 32 ++++++++--------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c5d84fbfcd2cd..23ac0f1fd4d7a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24512,9 +24512,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferFromTypes((source as IndexedAccessType).indexType, (target as IndexedAccessType).indexType); } else if (!(priority & InferencePriority.NakedTypeVariable) && target.flags & TypeFlags.IndexedAccess) { - const targetConstraint = (target as IndexedAccessType).objectType; - const inference = getInferenceInfoForType(getActualTypeVariable(targetConstraint)); + if (isFromInferenceBlockedSource(source)) { + return; + } + const inference = getInferenceInfoForType(getActualTypeVariable((target as IndexedAccessType).objectType)); if (inference) { + if (getObjectFlags(source) & ObjectFlags.NonInferrableType || source === nonInferrableAnyType) { + return; + } if (!inference.isFixed) { // Instantiates instance of `type PartialInference = ({[K in Keys]: {[K1 in K]: T}})[Keys];` // Where `T` is `source` and `Keys` is `target.indexType` @@ -24525,7 +24530,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inference.indexes = append(inference.indexes, instantiateType(inferenceType, mapper)); } } - return; } } else if (source.flags & TypeFlags.StringMapping && target.flags & TypeFlags.StringMapping) { diff --git a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt index 26bf596fa531b..335f0febc862f 100644 --- a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt +++ b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.errors.txt @@ -1,12 +1,10 @@ -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(24,42): error TS7006: Parameter 'value' implicitly has an 'any' type. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,36): error TS2769: No overload matches this call. Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. - Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. - Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. + Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. + Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. Type 'void' is not assignable to type 'boolean'. Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. - Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(27,42): error TS7006: Parameter 'value' implicitly has an 'any' type. + Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(43,41): error TS2769: No overload matches this call. Overload 1 of 2, '(props: Readonly): FieldFeedbackBeta', gave the following error. Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. @@ -14,17 +12,15 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(43,41): error TS2769: Type 'void' is not assignable to type 'boolean'. Overload 2 of 2, '(props: Props, context?: any): FieldFeedbackBeta', gave the following error. Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(61,43): error TS7006: Parameter 'value' implicitly has an 'any' type. tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,37): error TS2769: No overload matches this call. Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. - Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. + Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. Type 'void' is not assignable to type 'boolean'. Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. - Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. -tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: Parameter 'value' implicitly has an 'any' type. + Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. -==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (7 errors) ==== +==== tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx (3 errors) ==== /// import React from 'react'; @@ -49,23 +45,19 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: // OK const Test1 = () => !!value} />; - ~~~~~ -!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // Error: Void not assignable to boolean const Test2 = () => console.log(value)} />; ~~~~ !!! error TS2769: No overload matches this call. !!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback', gave the following error. -!!! error TS2769: Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. -!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. !!! error TS2769: Type 'void' is not assignable to type 'boolean'. !!! error TS2769: Overload 2 of 2, '(props: Props, context?: any): FieldFeedback', gave the following error. -!!! error TS2769: Type '(value: any) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '"a" | "b" | ((value: string) => boolean) | undefined'. !!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' !!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:6:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' - ~~~~~ -!!! error TS7006: Parameter 'value' implicitly has an 'any' type. class FieldFeedbackBeta

extends React.Component

{ static defaultProps: BaseProps = { @@ -110,22 +102,18 @@ tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx(64,43): error TS7006: // OK const Test3 = () => !!value} />; - ~~~~~ -!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // Error: Void not assignable to boolean const Test4 = () => console.log(value)} />; ~~~~ !!! error TS2769: No overload matches this call. !!! error TS2769: Overload 1 of 2, '(props: Readonly): FieldFeedback2', gave the following error. -!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. !!! error TS2769: Type 'void' is not assignable to type 'boolean'. !!! error TS2769: Overload 2 of 2, '(props: MyPropsProps, context?: any): FieldFeedback2', gave the following error. -!!! error TS2769: Type '(value: any) => void' is not assignable to type '(value: string) => boolean'. +!!! error TS2769: Type '(value: string) => void' is not assignable to type '(value: string) => boolean'. !!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' !!! related TS6500 tests/cases/compiler/reactDefaultPropsInferenceSuccess.tsx:46:3: The expected type comes from property 'when' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes> & Pick & Readonly, "children" | "error"> & Partial & Readonly, "when">> & Partial boolean; }, never>>' - ~~~~~ -!!! error TS7006: Parameter 'value' implicitly has an 'any' type. // OK const Test5 = () => ; diff --git a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types index 289c831bc55a1..9995e298150c5 100644 --- a/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types +++ b/tests/baselines/reference/reactDefaultPropsInferenceSuccess.types @@ -49,12 +49,12 @@ const Test1 = () => !!value} />; >() => !!value} /> : () => JSX.Element > !!value} /> : JSX.Element >FieldFeedback : typeof FieldFeedback ->when : (value: any) => boolean ->value => !!value : (value: any) => boolean ->value : any +>when : (value: string) => boolean +>value => !!value : (value: string) => boolean +>value : string >!!value : boolean >!value : boolean ->value : any +>value : string // Error: Void not assignable to boolean const Test2 = () => console.log(value)} />; @@ -62,14 +62,14 @@ const Test2 = () => console.log(value)} />; >() => console.log(value)} /> : () => JSX.Element > console.log(value)} /> : JSX.Element >FieldFeedback : typeof FieldFeedback ->when : (value: any) => void ->value => console.log(value) : (value: any) => void ->value : any +>when : (value: string) => void +>value => console.log(value) : (value: string) => void +>value : string >console.log(value) : void >console.log : (...data: any[]) => void >console : Console >log : (...data: any[]) => void ->value : any +>value : string class FieldFeedbackBeta

extends React.Component

{ >FieldFeedbackBeta : FieldFeedbackBeta

@@ -176,12 +176,12 @@ const Test3 = () => !!value} />; >() => !!value} /> : () => JSX.Element > !!value} /> : JSX.Element >FieldFeedback2 : typeof FieldFeedback2 ->when : (value: any) => boolean ->value => !!value : (value: any) => boolean ->value : any +>when : (value: string) => boolean +>value => !!value : (value: string) => boolean +>value : string >!!value : boolean >!value : boolean ->value : any +>value : string // Error: Void not assignable to boolean const Test4 = () => console.log(value)} />; @@ -189,14 +189,14 @@ const Test4 = () => console.log(value)} />; >() => console.log(value)} /> : () => JSX.Element > console.log(value)} /> : JSX.Element >FieldFeedback2 : typeof FieldFeedback2 ->when : (value: any) => void ->value => console.log(value) : (value: any) => void ->value : any +>when : (value: string) => void +>value => console.log(value) : (value: string) => void +>value : string >console.log(value) : void >console.log : (...data: any[]) => void >console : Console >log : (...data: any[]) => void ->value : any +>value : string // OK const Test5 = () => ; From 9825bcdfe830bde10102d477c882bb5e14c629c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 16 Sep 2024 14:06:19 +0200 Subject: [PATCH 18/18] update baselines --- ...completionsCommitCharactersGlobal.baseline | 414 ++++++++++++++++++ .../dependentDestructuredVariables.types | 2 +- ...ullAssertionPropegatesContextualType.types | 3 - ...rtProvider_namespaceSameNameAsIntrinsic.js | 6 + .../pasteEdits_revertUpdatedFile.js | 6 + 5 files changed, 427 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/completionsCommitCharactersGlobal.baseline b/tests/baselines/reference/completionsCommitCharactersGlobal.baseline index 03147604cbf99..787c5b8ee480a 100644 --- a/tests/baselines/reference/completionsCommitCharactersGlobal.baseline +++ b/tests/baselines/reference/completionsCommitCharactersGlobal.baseline @@ -2692,6 +2692,7 @@ // | type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void // | type Parameters any> = T extends (...args: infer P) => any ? P : never // | type Partial = { [P in keyof T]?: T[P]; } +// | type PartialInference = { [K in Keys]: { [K1 in K]: T; }; }[Keys] // | type Pick = { [P in K]: T[P]; } // | interface Promise // | type PromiseConstructorLike = new (executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void) => PromiseLike @@ -2872,6 +2873,7 @@ // | type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void // | type Parameters any> = T extends (...args: infer P) => any ? P : never // | type Partial = { [P in keyof T]?: T[P]; } +// | type PartialInference = { [K in Keys]: { [K1 in K]: T; }; }[Keys] // | type Pick = { [P in K]: T[P]; } // | interface Promise // | type PromiseConstructorLike = new (executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void) => PromiseLike @@ -73582,6 +73584,212 @@ } ] }, + { + "name": "PartialInference", + "kind": "type", + "kindModifiers": "declare", + "sortText": "15", + "displayParts": [ + { + "text": "type", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "PartialInference", + "kind": "aliasName" + }, + { + "text": "<", + "kind": "punctuation" + }, + { + "text": "T", + "kind": "typeParameterName" + }, + { + "text": ",", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "extends", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ">", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "=", + "kind": "operator" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "K", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "in", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "K1", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "in", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "K", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "T", + "kind": "typeParameterName" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + } + ], + "documentation": [ + { + "text": "Type instantiated to perform partial inferences from indexed accesses", + "kind": "text" + } + ] + }, { "name": "Pick", "kind": "type", @@ -82569,6 +82777,212 @@ } ] }, + { + "name": "PartialInference", + "kind": "type", + "kindModifiers": "declare", + "sortText": "15", + "displayParts": [ + { + "text": "type", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "PartialInference", + "kind": "aliasName" + }, + { + "text": "<", + "kind": "punctuation" + }, + { + "text": "T", + "kind": "typeParameterName" + }, + { + "text": ",", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "extends", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "string", + "kind": "keyword" + }, + { + "text": ">", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "=", + "kind": "operator" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "K", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "in", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "K1", + "kind": "typeParameterName" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "in", + "kind": "keyword" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "K", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "T", + "kind": "typeParameterName" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "}", + "kind": "punctuation" + }, + { + "text": "[", + "kind": "punctuation" + }, + { + "text": "Keys", + "kind": "typeParameterName" + }, + { + "text": "]", + "kind": "punctuation" + } + ], + "documentation": [ + { + "text": "Type instantiated to perform partial inferences from indexed accesses", + "kind": "text" + } + ] + }, { "name": "Pick", "kind": "type", diff --git a/tests/baselines/reference/dependentDestructuredVariables.types b/tests/baselines/reference/dependentDestructuredVariables.types index 1b829f101d426..ef798f07a45a7 100644 --- a/tests/baselines/reference/dependentDestructuredVariables.types +++ b/tests/baselines/reference/dependentDestructuredVariables.types @@ -2,7 +2,7 @@ === Performance Stats === Type Count: 2,500 -Instantiation count: 5,000 +Instantiation count: 2,500 -> 5,000 === dependentDestructuredVariables.ts === type Action = diff --git a/tests/baselines/reference/nonnullAssertionPropegatesContextualType.types b/tests/baselines/reference/nonnullAssertionPropegatesContextualType.types index 8424e057fb0ad..59010f1f05e57 100644 --- a/tests/baselines/reference/nonnullAssertionPropegatesContextualType.types +++ b/tests/baselines/reference/nonnullAssertionPropegatesContextualType.types @@ -1,8 +1,5 @@ //// [tests/cases/compiler/nonnullAssertionPropegatesContextualType.ts] //// -=== Performance Stats === -Type Count: 1,000 - === nonnullAssertionPropegatesContextualType.ts === let rect2: SVGRectElement = document.querySelector('.svg-rectangle')!; // Error: Element >rect2 : SVGRectElement diff --git a/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js b/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js index 5fa9b66ccb854..eb630a49266cf 100644 --- a/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js +++ b/tests/baselines/reference/tsserver/fourslashServer/autoImportProvider_namespaceSameNameAsIntrinsic.js @@ -976,6 +976,12 @@ Info seq [hh:mm:ss:mss] response: "kindModifiers": "declare", "sortText": "15" }, + { + "name": "PartialInference", + "kind": "type", + "kindModifiers": "declare", + "sortText": "15" + }, { "name": "Pick", "kind": "type", diff --git a/tests/baselines/reference/tsserver/fourslashServer/pasteEdits_revertUpdatedFile.js b/tests/baselines/reference/tsserver/fourslashServer/pasteEdits_revertUpdatedFile.js index b48a1f777b87a..395de616d3aba 100644 --- a/tests/baselines/reference/tsserver/fourslashServer/pasteEdits_revertUpdatedFile.js +++ b/tests/baselines/reference/tsserver/fourslashServer/pasteEdits_revertUpdatedFile.js @@ -928,6 +928,12 @@ Info seq [hh:mm:ss:mss] response: "kindModifiers": "declare", "sortText": "15" }, + { + "name": "PartialInference", + "kind": "type", + "kindModifiers": "declare", + "sortText": "15" + }, { "name": "Pick", "kind": "type",