Skip to content

Type of a function argument not inferred from context when defined using conditional typeΒ #46310

Closed
@Andarist

Description

@Andarist

Bug Report

πŸ”Ž Search Terms

inference conditional function parameters contextual optional property

πŸ•— Version & Regression Information

This is the behavior present in all 4.x versions available on the playground, including 4.5-beta.

⏯ Playground Link

Slimmed down repro case

Playground resembling a real-life use case

πŸ’» Code

export interface TypegenDisabled { "@@xstate/typegen": false; }
export interface TypegenEnabled { "@@xstate/typegen": true; }

type ActionFunction<TEvent extends { type: string }> = (event: TEvent) => void;

// this is supposed to "route" the second argument based on the information provided within the first one
declare function createMachine<
  TTypesMeta extends TypegenEnabled | TypegenDisabled = TypegenDisabled
>(
  config: {
    types?: TTypesMeta;
  },
  implementations: TTypesMeta extends TypegenEnabled
    ? ActionFunction<{ type: "test" }>
    : ActionFunction<{ type: string }>
): void;

// it doesn't work with inference, this doesn't contain explicit type param and the optional `types` property ain't used explicitly
createMachine({
  // if we uncomment this property it starts to work
  // types: {} as TypegenDisabled
}, (ev) => {
  // `ev` is implicitly typed as `any`  
  ev.type;
  // @ts-expect-error (it doesn't error since `ev` is `any`)
  ev.unknown;
  // @ts-expect-error (it doesn't error since `ev` is `any`)
  ((_type: "test") => null)(ev.type);
});

πŸ™ Actual behavior

The inferred type of this function is basically "AnyFunction" because the inference hits assignNonContextualParameterTypes and not explicit type params are given here.

From what I've seen in the debugger (when debugging the code from which this minimal repro has been created) the contextualSignature for this gets computed as undefined. This happens because the conditional type is "converted" to a union and the functions in the resulting union have no sufficient overlap (they are failing compareSignaturesIdentical check).

However, this feels like a piece of information about what happens "later on" and how the implicit any has been computed here. The issue I'm reporting is that this conditional type cannot be resolved "soon enough" for the correct branch from it to be chosen before we get to computing the contextualSignature here.

πŸ™‚ Expected behavior

I would expect TTypesMeta to be inferred as TypegenDisabled regardless of the types property in the second argument being in the object explicitly or not. Since it's an optional property its type should be inferred here as the default given in the generic signature.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issueNeeds InvestigationThis issue needs a team member to investigate its status.RescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions