From fbdca8412e04056ea8f4e6f5753c73629f1283a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sat, 15 Feb 2025 21:41:39 +0100 Subject: [PATCH 1/4] Contextual typing for return expressions of functions with contextual signatures based on instantiated types --- src/compiler/checker.ts | 13 ++- src/compiler/types.ts | 8 +- .../instantiateContextualTypes2.symbols | 73 ++++++++++++ .../instantiateContextualTypes2.types | 108 ++++++++++++++++++ .../compiler/instantiateContextualTypes2.ts | 30 +++++ 5 files changed, 222 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/instantiateContextualTypes2.symbols create mode 100644 tests/baselines/reference/instantiateContextualTypes2.types create mode 100644 tests/cases/compiler/instantiateContextualTypes2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index abd6fd4bf2e8d..f926864da6294 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1334,6 +1334,7 @@ export const enum CheckMode { // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, // we need to preserve generic types instead of substituting them for constraints TypeOnly = 1 << 6, // Called from getTypeOfExpression, diagnostics may be omitted + ContextualSignatureReturn = 1 << 7 // Checking return expressions of functions with contextual signatures } /** @internal */ @@ -31979,7 +31980,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If no inferences have been made, and none of the type parameters for which we are inferring // specify default types, nothing is gained from instantiating as type parameters would just be // replaced with their constraints similar to the apparent type. - if (inferenceContext && contextFlags! & ContextFlags.Signature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { + if (inferenceContext && contextFlags! & ContextFlags.ContextualSignature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { // For contextual signatures we incorporate all inferences made so far, e.g. from return // types as well as arguments to the left in a function call. return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); @@ -32446,7 +32447,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeTagSignature) { return typeTagSignature; } - const type = getApparentTypeOfContextualType(node, ContextFlags.Signature); + const type = getApparentTypeOfContextualType(node, ContextFlags.ContextualSignature); if (!type) { return undefined; } @@ -38858,7 +38859,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfSymbol(getSymbolOfDeclaration(node)); } - function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode?: CheckMode) { + function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode = CheckMode.Normal) { const links = getNodeLinks(node); // Check if function expression is contextually typed and assign parameter types if so. if (!(links.flags & NodeCheckFlags.ContextChecked)) { @@ -38899,7 +38900,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) { - const returnType = getReturnTypeFromBody(node, checkMode); + const returnType = getReturnTypeFromBody(node, checkMode | CheckMode.ContextualSignatureReturn); if (!signature.resolvedReturnType) { signature.resolvedReturnType = returnType; } @@ -40701,11 +40702,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } - function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { + function checkExpressionForMutableLocation(node: Expression, checkMode = CheckMode.Normal, forceTuple?: boolean): Type { const type = checkExpression(node, checkMode, forceTuple); return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) : isTypeAssertion(node) ? type : - getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(getContextualType(node, /*contextFlags*/ undefined), node, /*contextFlags*/ undefined)); + getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(getContextualType(node, checkMode & CheckMode.ContextualSignatureReturn ? ContextFlags.ContextualSignature : ContextFlags.None), node, /*contextFlags*/ undefined)); } function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f6ca75a68e17d..c8bf729865b23 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5461,10 +5461,10 @@ export const enum IntersectionFlags { // dprint-ignore /** @internal */ export const enum ContextFlags { - None = 0, - Signature = 1 << 0, // Obtaining contextual signature - NoConstraints = 1 << 1, // Don't obtain type variable constraints - Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions + None = 0, + ContextualSignature = 1 << 0, // Obtaining contextual signature or checking its return type + NoConstraints = 1 << 1, // Don't obtain type variable constraints + Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions SkipBindingPatterns = 1 << 3, // Ignore contextual types applied by binding patterns } diff --git a/tests/baselines/reference/instantiateContextualTypes2.symbols b/tests/baselines/reference/instantiateContextualTypes2.symbols new file mode 100644 index 0000000000000..22322b4397579 --- /dev/null +++ b/tests/baselines/reference/instantiateContextualTypes2.symbols @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/instantiateContextualTypes2.ts] //// + +=== instantiateContextualTypes2.ts === +type ContextStates = +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + + | { + status: "loading"; +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 1, 5)) + + data: null; +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 2, 24)) + } + | { + status: "success"; +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 5, 5)) + + data: string; +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 6, 24)) + + }; + +declare function createStore(config: { +>createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) +>config : Symbol(config, Decl(instantiateContextualTypes2.ts, 10, 39)) + + context: TContext; +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 10, 48)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) + + on: Record TContext>; +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 11, 20)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 12, 22)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) + +}): void; + +const store = createStore({ +>store : Symbol(store, Decl(instantiateContextualTypes2.ts, 15, 5)) +>createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) + + context: { +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 15, 27)) + + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 16, 12)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 17, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 19, 21)) + + fetch: (ctx) => ({ +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 20, 7)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 21, 12)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 21, 22)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 22, 24)) + + }), + }, +}); + diff --git a/tests/baselines/reference/instantiateContextualTypes2.types b/tests/baselines/reference/instantiateContextualTypes2.types new file mode 100644 index 0000000000000..d25f7dd6f7a99 --- /dev/null +++ b/tests/baselines/reference/instantiateContextualTypes2.types @@ -0,0 +1,108 @@ +//// [tests/cases/compiler/instantiateContextualTypes2.ts] //// + +=== instantiateContextualTypes2.ts === +type ContextStates = +>ContextStates : ContextStates +> : ^^^^^^^^^^^^^ + + | { + status: "loading"; +>status : "loading" +> : ^^^^^^^^^ + + data: null; +>data : null +> : ^^^^ + } + | { + status: "success"; +>status : "success" +> : ^^^^^^^^^ + + data: string; +>data : string +> : ^^^^^^ + + }; + +declare function createStore(config: { +>createStore : (config: { context: TContext; on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^^^^ +>config : { context: TContext; on: Record TContext>; } +> : ^^^^^^^^^^^ ^^^^^^ ^^^ + + context: TContext; +>context : TContext +> : ^^^^^^^^ + + on: Record TContext>; +>on : Record TContext> +> : ^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^ +>ctx : TContext +> : ^^^^^^^^ + +}): void; + +const store = createStore({ +>store : void +> : ^^^^ +>createStore({ context: { status: "loading", data: null, } as ContextStates, on: { fetch: (ctx) => ({ status: "success", data: "hello", }), },}) : void +> : ^^^^ +>createStore : (config: { context: TContext; on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^^^^ +>{ context: { status: "loading", data: null, } as ContextStates, on: { fetch: (ctx) => ({ status: "success", data: "hello", }), },} : { context: ContextStates; on: { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + on: { +>on : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: (ctx) => ({ status: "success", data: "hello", }), } : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: (ctx) => ({ +>fetch : (ctx: ContextStates) => { status: "success"; data: string; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(ctx) => ({ status: "success", data: "hello", }) : (ctx: ContextStates) => { status: "success"; data: string; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ctx : ContextStates +> : ^^^^^^^^^^^^^ +>({ status: "success", data: "hello", }) : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }), + }, +}); + diff --git a/tests/cases/compiler/instantiateContextualTypes2.ts b/tests/cases/compiler/instantiateContextualTypes2.ts new file mode 100644 index 0000000000000..2588c6d454cb7 --- /dev/null +++ b/tests/cases/compiler/instantiateContextualTypes2.ts @@ -0,0 +1,30 @@ +// @strict: true +// @noEmit: true + +type ContextStates = + | { + status: "loading"; + data: null; + } + | { + status: "success"; + data: string; + }; + +declare function createStore(config: { + context: TContext; + on: Record TContext>; +}): void; + +const store = createStore({ + context: { + status: "loading", + data: null, + } as ContextStates, + on: { + fetch: (ctx) => ({ + status: "success", + data: "hello", + }), + }, +}); From 17fa1c448f8d6cf9b67c7e475fe9ae85824c87b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Feb 2025 15:45:53 +0100 Subject: [PATCH 2/4] remove redundant check mode --- src/compiler/checker.ts | 28 +- .../instantiateContextualTypes2.symbols | 310 +++++++++- .../instantiateContextualTypes2.types | 563 ++++++++++++++++-- .../compiler/instantiateContextualTypes2.ts | 156 ++++- 4 files changed, 978 insertions(+), 79 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f926864da6294..27493278c5f4e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1334,7 +1334,6 @@ export const enum CheckMode { // e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`, // we need to preserve generic types instead of substituting them for constraints TypeOnly = 1 << 6, // Called from getTypeOfExpression, diagnostics may be omitted - ContextualSignatureReturn = 1 << 7 // Checking return expressions of functions with contextual signatures } /** @internal */ @@ -31972,6 +31971,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function isReturnExpressionLiteralContext(node: Node) { + const ancestor = findAncestor(node, n => { + if (isCallOrNewExpression(n)) { + return "quit"; + } + const parent = n.parent; + if (isStatement(parent)) { + return parent.kind === SyntaxKind.ReturnStatement || "quit"; + } + if (parent.kind === SyntaxKind.ArrowFunction) { + return (parent as ArrowFunction).body === n || "quit"; + } + return false; + }); + return !!(ancestor && isExpression(ancestor) && getContextualTypeForReturnExpression(ancestor, /*contextFlags*/ undefined)); + } + // If the given contextual type contains instantiable types and if a mapper representing // return type inferences is available, instantiate those types using that mapper. function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags: ContextFlags | undefined): Type | undefined { @@ -31980,7 +31996,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If no inferences have been made, and none of the type parameters for which we are inferring // specify default types, nothing is gained from instantiating as type parameters would just be // replaced with their constraints similar to the apparent type. - if (inferenceContext && contextFlags! & ContextFlags.ContextualSignature && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { + if (inferenceContext && (contextFlags! & ContextFlags.ContextualSignature || isReturnExpressionLiteralContext(node)) && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { // For contextual signatures we incorporate all inferences made so far, e.g. from return // types as well as arguments to the left in a function call. return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); @@ -38859,7 +38875,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeOfSymbol(getSymbolOfDeclaration(node)); } - function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode = CheckMode.Normal) { + function contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | ArrowFunction | MethodDeclaration, checkMode?: CheckMode) { const links = getNodeLinks(node); // Check if function expression is contextually typed and assign parameter types if so. if (!(links.flags & NodeCheckFlags.ContextChecked)) { @@ -38900,7 +38916,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) { - const returnType = getReturnTypeFromBody(node, checkMode | CheckMode.ContextualSignatureReturn); + const returnType = getReturnTypeFromBody(node, checkMode); if (!signature.resolvedReturnType) { signature.resolvedReturnType = returnType; } @@ -40702,11 +40718,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } - function checkExpressionForMutableLocation(node: Expression, checkMode = CheckMode.Normal, forceTuple?: boolean): Type { + function checkExpressionForMutableLocation(node: Expression, checkMode?: CheckMode, forceTuple?: boolean): Type { const type = checkExpression(node, checkMode, forceTuple); return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) : isTypeAssertion(node) ? type : - getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(getContextualType(node, checkMode & CheckMode.ContextualSignatureReturn ? ContextFlags.ContextualSignature : ContextFlags.None), node, /*contextFlags*/ undefined)); + getWidenedLiteralLikeTypeForContextualType(type, instantiateContextualType(getContextualType(node, /*contextFlags*/ undefined), node, /*contextFlags*/ undefined)); } function checkPropertyAssignment(node: PropertyAssignment, checkMode?: CheckMode): Type { diff --git a/tests/baselines/reference/instantiateContextualTypes2.symbols b/tests/baselines/reference/instantiateContextualTypes2.symbols index 22322b4397579..a26749b566fc0 100644 --- a/tests/baselines/reference/instantiateContextualTypes2.symbols +++ b/tests/baselines/reference/instantiateContextualTypes2.symbols @@ -20,54 +20,314 @@ type ContextStates = }; -declare function createStore(config: { +declare function createStore( >createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) >TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) ->config : Symbol(config, Decl(instantiateContextualTypes2.ts, 10, 39)) - context: TContext; ->context : Symbol(context, Decl(instantiateContextualTypes2.ts, 10, 48)) + context: TContext, +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 10, 39)) >TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) - on: Record TContext>; ->on : Symbol(on, Decl(instantiateContextualTypes2.ts, 11, 20)) + config: { +>config : Symbol(config, Decl(instantiateContextualTypes2.ts, 11, 20)) + + on: Record TContext>; +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 12, 11)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) ->ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 12, 22)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 13, 24)) >TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) >TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 10, 29)) -}): void; + }, +): void; -const store = createStore({ ->store : Symbol(store, Decl(instantiateContextualTypes2.ts, 15, 5)) +const store1 = createStore( +>store1 : Symbol(store1, Decl(instantiateContextualTypes2.ts, 17, 5)) >createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 18, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 19, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 22, 3)) + + fetch: (ctx) => ({ +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 23, 9)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 24, 14)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 24, 24)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 25, 26)) + + }), + }, + }, +); + +const store2 = createStore( +>store2 : Symbol(store2, Decl(instantiateContextualTypes2.ts, 32, 5)) +>createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 33, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 34, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 37, 3)) + + fetch: () => ({ +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 38, 9)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 39, 21)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 40, 26)) + + }), + }, + }, +); + +const store3 = createStore( +>store3 : Symbol(store3, Decl(instantiateContextualTypes2.ts, 47, 5)) +>createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 48, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 49, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 52, 3)) + + fetch: (ctx) => { +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 53, 9)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 54, 14)) + + return { + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 55, 16)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 56, 28)) + + }; + }, + }, + }, +); + +const store4 = createStore( +>store4 : Symbol(store4, Decl(instantiateContextualTypes2.ts, 64, 5)) +>createStore : Symbol(createStore, Decl(instantiateContextualTypes2.ts, 8, 6)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 65, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 66, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 69, 3)) + + fetch: () => { +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 70, 9)) + + return { + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 72, 16)) - context: { ->context : Symbol(context, Decl(instantiateContextualTypes2.ts, 15, 27)) + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 73, 28)) + + }; + }, + }, + }, +); + +declare function createStore2( +>createStore2 : Symbol(createStore2, Decl(instantiateContextualTypes2.ts, 79, 2)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 81, 30)) + + context: TContext, +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 81, 40)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 81, 30)) + + config: { +>config : Symbol(config, Decl(instantiateContextualTypes2.ts, 82, 20)) + + on: Record { context: TContext }>; +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 83, 11)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 84, 24)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 81, 30)) +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 84, 43)) +>TContext : Symbol(TContext, Decl(instantiateContextualTypes2.ts, 81, 30)) + + }, +): void; + +const store5 = createStore2( +>store5 : Symbol(store5, Decl(instantiateContextualTypes2.ts, 88, 5)) +>createStore2 : Symbol(createStore2, Decl(instantiateContextualTypes2.ts, 79, 2)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 89, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 90, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 93, 3)) + + fetch: (ctx) => ({ +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 94, 9)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 95, 14)) + + context: { +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 95, 24)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 96, 18)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 97, 28)) + + }, + }), + }, + }, +); + +const store6 = createStore2( +>store6 : Symbol(store6, Decl(instantiateContextualTypes2.ts, 105, 5)) +>createStore2 : Symbol(createStore2, Decl(instantiateContextualTypes2.ts, 79, 2)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 106, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 107, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 110, 3)) + + fetch: () => ({ +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 111, 9)) + + context: { +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 112, 21)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 113, 18)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 114, 28)) + + }, + }), + }, + }, +); + +const store7 = createStore2( +>store7 : Symbol(store7, Decl(instantiateContextualTypes2.ts, 122, 5)) +>createStore2 : Symbol(createStore2, Decl(instantiateContextualTypes2.ts, 79, 2)) + { + status: "loading", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 123, 3)) + + data: null, +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 124, 22)) + + } as ContextStates, +>ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 127, 3)) + + fetch: (ctx) => { +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 128, 9)) +>ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 129, 14)) + + return { + context: { +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 130, 16)) + + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 131, 20)) + + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 132, 30)) + + }, + }; + }, + }, + }, +); +const store8 = createStore2( +>store8 : Symbol(store8, Decl(instantiateContextualTypes2.ts, 141, 5)) +>createStore2 : Symbol(createStore2, Decl(instantiateContextualTypes2.ts, 79, 2)) + { status: "loading", ->status : Symbol(status, Decl(instantiateContextualTypes2.ts, 16, 12)) +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 142, 3)) data: null, ->data : Symbol(data, Decl(instantiateContextualTypes2.ts, 17, 22)) +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 143, 22)) } as ContextStates, >ContextStates : Symbol(ContextStates, Decl(instantiateContextualTypes2.ts, 0, 0)) + { + on: { +>on : Symbol(on, Decl(instantiateContextualTypes2.ts, 146, 3)) - on: { ->on : Symbol(on, Decl(instantiateContextualTypes2.ts, 19, 21)) + fetch: () => { +>fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 147, 9)) - fetch: (ctx) => ({ ->fetch : Symbol(fetch, Decl(instantiateContextualTypes2.ts, 20, 7)) ->ctx : Symbol(ctx, Decl(instantiateContextualTypes2.ts, 21, 12)) + return { + context: { +>context : Symbol(context, Decl(instantiateContextualTypes2.ts, 149, 16)) - status: "success", ->status : Symbol(status, Decl(instantiateContextualTypes2.ts, 21, 22)) + status: "success", +>status : Symbol(status, Decl(instantiateContextualTypes2.ts, 150, 20)) - data: "hello", ->data : Symbol(data, Decl(instantiateContextualTypes2.ts, 22, 24)) + data: "hello", +>data : Symbol(data, Decl(instantiateContextualTypes2.ts, 151, 30)) - }), + }, + }; + }, + }, }, -}); +); diff --git a/tests/baselines/reference/instantiateContextualTypes2.types b/tests/baselines/reference/instantiateContextualTypes2.types index d25f7dd6f7a99..8d4cd4942956c 100644 --- a/tests/baselines/reference/instantiateContextualTypes2.types +++ b/tests/baselines/reference/instantiateContextualTypes2.types @@ -25,37 +25,159 @@ type ContextStates = }; -declare function createStore(config: { ->createStore : (config: { context: TContext; on: Record TContext>; }) => void -> : ^ ^^ ^^ ^^^^^ ->config : { context: TContext; on: Record TContext>; } -> : ^^^^^^^^^^^ ^^^^^^ ^^^ +declare function createStore( +>createStore : (context: TContext, config: { on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ - context: TContext; + context: TContext, >context : TContext > : ^^^^^^^^ - on: Record TContext>; + config: { +>config : { on: Record TContext>; } +> : ^^^^^^ ^^^ + + on: Record TContext>; >on : Record TContext> > : ^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^ >ctx : TContext > : ^^^^^^^^ -}): void; - -const store = createStore({ ->store : void -> : ^^^^ ->createStore({ context: { status: "loading", data: null, } as ContextStates, on: { fetch: (ctx) => ({ status: "success", data: "hello", }), },}) : void -> : ^^^^ ->createStore : (config: { context: TContext; on: Record TContext>; }) => void -> : ^ ^^ ^^ ^^^^^ ->{ context: { status: "loading", data: null, } as ContextStates, on: { fetch: (ctx) => ({ status: "success", data: "hello", }), },} : { context: ContextStates; on: { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; }; } -> : ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - context: { ->context : ContextStates -> : ^^^^^^^^^^^^^ + }, +): void; + +const store1 = createStore( +>store1 : void +> : ^^^^ +>createStore( { status: "loading", data: null, } as ContextStates, { on: { fetch: (ctx) => ({ status: "success", data: "hello", }), }, },) : void +> : ^^^^ +>createStore : (context: TContext, config: { on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: (ctx) => ({ status: "success", data: "hello", }), }, } : { on: { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: (ctx) => ({ status: "success", data: "hello", }), } : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: (ctx) => ({ +>fetch : (ctx: ContextStates) => { status: "success"; data: string; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(ctx) => ({ status: "success", data: "hello", }) : (ctx: ContextStates) => { status: "success"; data: string; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ctx : ContextStates +> : ^^^^^^^^^^^^^ +>({ status: "success", data: "hello", }) : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }), + }, + }, +); + +const store2 = createStore( +>store2 : void +> : ^^^^ +>createStore( { status: "loading", data: null, } as ContextStates, { on: { fetch: () => ({ status: "success", data: "hello", }), }, },) : void +> : ^^^^ +>createStore : (context: TContext, config: { on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: () => ({ status: "success", data: "hello", }), }, } : { on: { fetch: () => { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: () => { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: () => ({ status: "success", data: "hello", }), } : { fetch: () => { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: () => ({ +>fetch : () => { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => ({ status: "success", data: "hello", }) : () => { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>({ status: "success", data: "hello", }) : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }), + }, + }, +); + +const store3 = createStore( +>store3 : void +> : ^^^^ +>createStore( { status: "loading", data: null, } as ContextStates, { on: { fetch: (ctx) => { return { status: "success", data: "hello", }; }, }, },) : void +> : ^^^^ +>createStore : (context: TContext, config: { on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { >{ status: "loading", data: null, } as ContextStates : ContextStates > : ^^^^^^^^^^^^^ >{ status: "loading", data: null, } : { status: "loading"; data: null; } @@ -72,37 +194,406 @@ const store = createStore({ > : ^^^^ } as ContextStates, - on: { + { +>{ on: { fetch: (ctx) => { return { status: "success", data: "hello", }; }, }, } : { on: { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { >on : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } > : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->{ fetch: (ctx) => ({ status: "success", data: "hello", }), } : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } -> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: (ctx) => { return { status: "success", data: "hello", }; }, } : { fetch: (ctx: ContextStates) => { status: "success"; data: string; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - fetch: (ctx) => ({ + fetch: (ctx) => { >fetch : (ctx: ContextStates) => { status: "success"; data: string; } > : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->(ctx) => ({ status: "success", data: "hello", }) : (ctx: ContextStates) => { status: "success"; data: string; } -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(ctx) => { return { status: "success", data: "hello", }; } : (ctx: ContextStates) => { status: "success"; data: string; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >ctx : ContextStates > : ^^^^^^^^^^^^^ ->({ status: "success", data: "hello", }) : { status: "success"; data: string; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->{ status: "success", data: "hello", } : { status: "success"; data: string; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - status: "success", + return { +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }; + }, + }, + }, +); + +const store4 = createStore( +>store4 : void +> : ^^^^ +>createStore( { status: "loading", data: null, } as ContextStates, { on: { fetch: () => { return { status: "success", data: "hello", }; }, }, },) : void +> : ^^^^ +>createStore : (context: TContext, config: { on: Record TContext>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: () => { return { status: "success", data: "hello", }; }, }, } : { on: { fetch: () => { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: () => { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: () => { return { status: "success", data: "hello", }; }, } : { fetch: () => { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: () => { +>fetch : () => { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => { return { status: "success", data: "hello", }; } : () => { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return { +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }; + }, + }, + }, +); + +declare function createStore2( +>createStore2 : (context: TContext, config: { on: Record { context: TContext; }>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + + context: TContext, +>context : TContext +> : ^^^^^^^^ + + config: { +>config : { on: Record { context: TContext; }>; } +> : ^^^^^^ ^^^ + + on: Record { context: TContext }>; +>on : Record { context: TContext; }> +> : ^^^^^^^^^^^^^^^^ ^^ ^^^^^ ^ +>ctx : TContext +> : ^^^^^^^^ +>context : TContext +> : ^^^^^^^^ + + }, +): void; + +const store5 = createStore2( +>store5 : void +> : ^^^^ +>createStore2( { status: "loading", data: null, } as ContextStates, { on: { fetch: (ctx) => ({ context: { status: "success", data: "hello", }, }), }, },) : void +> : ^^^^ +>createStore2 : (context: TContext, config: { on: Record { context: TContext; }>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: (ctx) => ({ context: { status: "success", data: "hello", }, }), }, } : { on: { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; }; } +> : ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: (ctx) => ({ context: { status: "success", data: "hello", }, }), } : { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: (ctx) => ({ +>fetch : (ctx: ContextStates) => { context: { status: "success"; data: string; }; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(ctx) => ({ context: { status: "success", data: "hello", }, }) : (ctx: ContextStates) => { context: { status: "success"; data: string; }; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ctx : ContextStates +> : ^^^^^^^^^^^^^ +>({ context: { status: "success", data: "hello", }, }) : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ context: { status: "success", data: "hello", }, } : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }, + }), + }, + }, +); + +const store6 = createStore2( +>store6 : void +> : ^^^^ +>createStore2( { status: "loading", data: null, } as ContextStates, { on: { fetch: () => ({ context: { status: "success", data: "hello", }, }), }, },) : void +> : ^^^^ +>createStore2 : (context: TContext, config: { on: Record { context: TContext; }>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: () => ({ context: { status: "success", data: "hello", }, }), }, } : { on: { fetch: () => { context: { status: "success"; data: string; }; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: () => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: () => ({ context: { status: "success", data: "hello", }, }), } : { fetch: () => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: () => ({ +>fetch : () => { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => ({ context: { status: "success", data: "hello", }, }) : () => { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>({ context: { status: "success", data: "hello", }, }) : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ context: { status: "success", data: "hello", }, } : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }, + }), + }, + }, +); + +const store7 = createStore2( +>store7 : void +> : ^^^^ +>createStore2( { status: "loading", data: null, } as ContextStates, { on: { fetch: (ctx) => { return { context: { status: "success", data: "hello", }, }; }, }, },) : void +> : ^^^^ +>createStore2 : (context: TContext, config: { on: Record { context: TContext; }>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: (ctx) => { return { context: { status: "success", data: "hello", }, }; }, }, } : { on: { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; }; } +> : ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: (ctx) => { return { context: { status: "success", data: "hello", }, }; }, } : { fetch: (ctx: ContextStates) => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: (ctx) => { +>fetch : (ctx: ContextStates) => { context: { status: "success"; data: string; }; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(ctx) => { return { context: { status: "success", data: "hello", }, }; } : (ctx: ContextStates) => { context: { status: "success"; data: string; }; } +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>ctx : ContextStates +> : ^^^^^^^^^^^^^ + + return { +>{ context: { status: "success", data: "hello", }, } : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", +>status : "success" +> : ^^^^^^^^^ +>"success" : "success" +> : ^^^^^^^^^ + + data: "hello", +>data : string +> : ^^^^^^ +>"hello" : "hello" +> : ^^^^^^^ + + }, + }; + }, + }, + }, +); + +const store8 = createStore2( +>store8 : void +> : ^^^^ +>createStore2( { status: "loading", data: null, } as ContextStates, { on: { fetch: () => { return { context: { status: "success", data: "hello", }, }; }, }, },) : void +> : ^^^^ +>createStore2 : (context: TContext, config: { on: Record { context: TContext; }>; }) => void +> : ^ ^^ ^^ ^^ ^^ ^^^^^ + { +>{ status: "loading", data: null, } as ContextStates : ContextStates +> : ^^^^^^^^^^^^^ +>{ status: "loading", data: null, } : { status: "loading"; data: null; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "loading", +>status : "loading" +> : ^^^^^^^^^ +>"loading" : "loading" +> : ^^^^^^^^^ + + data: null, +>data : null +> : ^^^^ + + } as ContextStates, + { +>{ on: { fetch: () => { return { context: { status: "success", data: "hello", }, }; }, }, } : { on: { fetch: () => { context: { status: "success"; data: string; }; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + on: { +>on : { fetch: () => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ fetch: () => { return { context: { status: "success", data: "hello", }, }; }, } : { fetch: () => { context: { status: "success"; data: string; }; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + fetch: () => { +>fetch : () => { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>() => { return { context: { status: "success", data: "hello", }, }; } : () => { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return { +>{ context: { status: "success", data: "hello", }, } : { context: { status: "success"; data: string; }; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + context: { +>context : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ status: "success", data: "hello", } : { status: "success"; data: string; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + status: "success", >status : "success" > : ^^^^^^^^^ >"success" : "success" > : ^^^^^^^^^ - data: "hello", + data: "hello", >data : string > : ^^^^^^ >"hello" : "hello" > : ^^^^^^^ - }), + }, + }; + }, + }, }, -}); +); diff --git a/tests/cases/compiler/instantiateContextualTypes2.ts b/tests/cases/compiler/instantiateContextualTypes2.ts index 2588c6d454cb7..aa5abace0b24e 100644 --- a/tests/cases/compiler/instantiateContextualTypes2.ts +++ b/tests/cases/compiler/instantiateContextualTypes2.ts @@ -11,20 +11,152 @@ type ContextStates = data: string; }; -declare function createStore(config: { - context: TContext; - on: Record TContext>; -}): void; +declare function createStore( + context: TContext, + config: { + on: Record TContext>; + }, +): void; + +const store1 = createStore( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: (ctx) => ({ + status: "success", + data: "hello", + }), + }, + }, +); + +const store2 = createStore( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: () => ({ + status: "success", + data: "hello", + }), + }, + }, +); + +const store3 = createStore( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: (ctx) => { + return { + status: "success", + data: "hello", + }; + }, + }, + }, +); + +const store4 = createStore( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: () => { + return { + status: "success", + data: "hello", + }; + }, + }, + }, +); + +declare function createStore2( + context: TContext, + config: { + on: Record { context: TContext }>; + }, +): void; + +const store5 = createStore2( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: (ctx) => ({ + context: { + status: "success", + data: "hello", + }, + }), + }, + }, +); + +const store6 = createStore2( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: () => ({ + context: { + status: "success", + data: "hello", + }, + }), + }, + }, +); + +const store7 = createStore2( + { + status: "loading", + data: null, + } as ContextStates, + { + on: { + fetch: (ctx) => { + return { + context: { + status: "success", + data: "hello", + }, + }; + }, + }, + }, +); -const store = createStore({ - context: { +const store8 = createStore2( + { status: "loading", data: null, } as ContextStates, - on: { - fetch: (ctx) => ({ - status: "success", - data: "hello", - }), + { + on: { + fetch: () => { + return { + context: { + status: "success", + data: "hello", + }, + }; + }, + }, }, -}); +); From 4dce1de717055b4ea76e3d48e8f428b828ebef28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Feb 2025 15:47:45 +0100 Subject: [PATCH 3/4] revert some of the minor changes --- src/compiler/checker.ts | 6 +++--- src/compiler/types.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 27493278c5f4e..f71143655da77 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31996,7 +31996,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If no inferences have been made, and none of the type parameters for which we are inferring // specify default types, nothing is gained from instantiating as type parameters would just be // replaced with their constraints similar to the apparent type. - if (inferenceContext && (contextFlags! & ContextFlags.ContextualSignature || isReturnExpressionLiteralContext(node)) && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { + if (inferenceContext && (contextFlags! & ContextFlags.Signature || isReturnExpressionLiteralContext(node)) && some(inferenceContext.inferences, hasInferenceCandidatesOrDefault)) { // For contextual signatures we incorporate all inferences made so far, e.g. from return // types as well as arguments to the left in a function call. return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper); @@ -32463,7 +32463,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (typeTagSignature) { return typeTagSignature; } - const type = getApparentTypeOfContextualType(node, ContextFlags.ContextualSignature); + const type = getApparentTypeOfContextualType(node, ContextFlags.Signature); if (!type) { return undefined; } @@ -40718,7 +40718,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isPropertyAssignment(parent) || isShorthandPropertyAssignment(parent) || isTemplateSpan(parent)) && isConstContext(parent.parent); } - function checkExpressionForMutableLocation(node: Expression, checkMode?: CheckMode, forceTuple?: boolean): Type { + function checkExpressionForMutableLocation(node: Expression, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { const type = checkExpression(node, checkMode, forceTuple); return isConstContext(node) || isCommonJsExportedExpression(node) ? getRegularTypeOfLiteralType(type) : isTypeAssertion(node) ? type : diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c8bf729865b23..f6ca75a68e17d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5461,10 +5461,10 @@ export const enum IntersectionFlags { // dprint-ignore /** @internal */ export const enum ContextFlags { - None = 0, - ContextualSignature = 1 << 0, // Obtaining contextual signature or checking its return type - NoConstraints = 1 << 1, // Don't obtain type variable constraints - Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions + None = 0, + Signature = 1 << 0, // Obtaining contextual signature + NoConstraints = 1 << 1, // Don't obtain type variable constraints + Completions = 1 << 2, // Ignore inference to current node and parent nodes out to the containing call for completions SkipBindingPatterns = 1 << 3, // Ignore contextual types applied by binding patterns } From a45dfbb1057c127227c9a7734b114219db9b8caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Feb 2025 20:48:02 +0100 Subject: [PATCH 4/4] add comment --- src/compiler/checker.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f71143655da77..4dd8a8fc2acac 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -31974,6 +31974,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isReturnExpressionLiteralContext(node: Node) { const ancestor = findAncestor(node, n => { if (isCallOrNewExpression(n)) { + // prevent inference candidates of outer inference context to provide contextual type information for the expressions within the inner context + // that could turn fresh literal candidates in the inner context into regular types for union-like literals (such as booleans and enums) + // and that would create mismatches between inferred types for outer and inner contexts which is especially problematic when invariant type parameters are involved + // + // the call below should be ok but with the inner one receiving `boolean` as contextual type it would infer `true` for its type parameter + // and that would create outer signature applicability error with outer `Box` and inner `Box` + // + // interface Box { v: (arg: T) => T; } + // declare function invariantBox(v: T): Box + // declare function fn(arg: Box, get: () => Box): void; + // fn(invariantBox(true), () => invariantBox(true)); return "quit"; } const parent = n.parent;