Skip to content

silentNeverType leak through return type inferenceΒ #62824

@Andarist

Description

@Andarist

πŸ”Ž Search Terms

silent never leak reverse mapped types nested calls return type inference

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried

⏯ Playground Link

https://www.typescriptlang.org/play/?noEmit=true&ts=6.0.0-dev.20251202#code/C4TwDgpgBAaghgGwK4QM4B4AqA+KBeKTAbQGsIQB7AM0IF0BuAKEdEigFk4BjACwEsAdhADCFAcAgAPYPigAlCFwoAnACbpUwZYIDmAGihwBIbE0aCJyqt2gAFOMrgBbCJb4AvCKoDyAIwBWijIA3oxQUKwQAFxQmtoCOkzhYA7OqAD8MUgCJAIUAO4CTAC+zJFQAIJcwHxiAGLZ1bUC6GGEouJSMl0QAqqoHNz8Qh0S0nptmPaOTgM9fQPTzq4Q2p4+AUFEAEQpM6jbtFAAPlDZqhBUgl4T4ZhVNWJQ8-1QSy5u636B1RO4BKFwgAKaqSGKYUZdAx7NLg96oACUMQAbhQ+KoklAAPoUJDALH3JpiTKEB7NEpmcqYCjvFZrLzfIKtO60z5eThgZ7SXqvBRKNTM8KxLS6W5C1mrDwMzbVHYw2aHE5nPqXa6qNrYP6yeDINDoQFQIgAaSggigZEoNCmqQ+kvWHKgADJhfEdLQYgbwpEYkbMckbag4Ta6VLVBzjQw2sUSqYyuBoKIEAggmSxBhJpDpFyJAtBrxrpngGLrftszzFsG2RsfsA5QHFadzqqhKotQQgW1PVAlJ1xm1wr0AI4oFBi4oe-vdsRjYDgwt+54CYcQFAxIHcR4CNcI-C4VHond4PdojFRsXywOEeETQ-H9FmC5cBAOaBURqbxfLlCpgTpu6Fstc04fMRmnKFJnhQDXglelqy2XZ6yORsVSuFtix-KCKxmEMvhlGQCBg0NGV+RhsA7cIlCTIIYkTZNqh-DAITA8YrwDTVGCRSoiQEBoBG4rBCwMEs0iEn9Y0YR9n2UV932aWJXCQMBBSY3tum5IChgLZii0mBjMPkRQVHUSc4lFSdCNwms632BtlQuVCvA1WRgmKP4gQNSJUDFDdmi8xhxygDz4wyD0p1UkkVJnKBozaHy0xJLtjVNARzXIahSW41B3S4zdeP4yd-20sUhQyzdUAjYqhWpCzpRrLAGOwSdY3CGLik4-dT0YVAFLAdzYsyicStUCgAGVgCQKgqBiIcRwgBigSBLFoQDGIBCQJxfFWW9ArascJjasxuvG3qDTi39BqFYaxomqbP1m+bFuW-ZVvWzblG2lyEUq4bvGAHhVmuya1yWqALxiUyEg+1yz38hFDp6vrwjOy8uyu8agbulc5syhaQbBqA1o2rbdx2r7Jx+v6AfR27FpiIwQCe2EXV0KG9thpggA

πŸ’» Code

type Values<T> = T[keyof T];

type MachineContext = Record<string, any>;

interface ParameterizedObject {
  type: string;
  params?: unknown;
}

type ActionFunction<
  TContext extends MachineContext,
  TParams extends ParameterizedObject["params"] | undefined,
  TAction extends ParameterizedObject,
> = {
  (ctx: TContext, params: TParams): void;
  _out_TAction?: TAction;
};

type ToParameterizedObject<
  TParameterizedMap extends Record<
    string,
    ParameterizedObject["params"] | undefined
  >,
> = Values<{
  [K in keyof TParameterizedMap & string]: {
    type: K;
    params: TParameterizedMap[K];
  };
}>;

type CollectActions<
  TContext extends MachineContext,
  TParams extends ParameterizedObject["params"] | undefined,
> = (
  {
    context,
    enqueue,
  }: {
    context: TContext;
    enqueue: (action: () => void) => void;
  },
  params: TParams,
) => void;

declare function enqueueActions<
  TContext extends MachineContext,
  TParams extends ParameterizedObject["params"] | undefined,
  TAction extends ParameterizedObject = ParameterizedObject,
>(
  collect: CollectActions<TContext, TParams>,
): ActionFunction<TContext, TParams, TAction>;

declare function setup<
  TContext extends MachineContext,
  TActions extends Record<
    string,
    ParameterizedObject["params"] | undefined
  > = {},
>({
  types,
  actions,
}: {
  types?: { context?: TContext };
  actions?: {
    [K in keyof TActions]: ActionFunction<
      TContext,
      TActions[K],
      ToParameterizedObject<TActions>
    >;
  };
}): void;

setup({
  actions: {
    doStuff: enqueueActions((_, params: number) => {}),
  },
});

setup({
  actions: {
    doStuff: enqueueActions((_, params: number) => {}),
    doOtherStuff: (_, params: string) => {},
  },
});

setup({
  actions: {
    doStuff: enqueueActions((_, params: number) => {}),
    doOtherStuff: (_: any, params: string) => {},
  },
});

πŸ™ Actual behavior

First 2 calls mention such a relationship check failure in the error message:

Type 'ActionFunction<MachineContext, number, { type: string; params: never; }>' is not assignable to type 'ActionFunction<MachineContext, number, { type: "doStuff"; params: number; }>'.

Notice never there. This is quite weird an unexpected as there is no never in sight here. It turns out this is a silentNeverType that leaked through.

πŸ™‚ Expected behavior

At the very least, I would expect it to error with consistent error messages mentioning unknown instead of never. Even better, if it could behave closer to a very similar version of this code: TS playground. In there the third error still errors but the first two infers nicely.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: check: Type InferenceRelated to type inference performed during signature resolution or `infer` type resolutionHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions