gvergnaud / ts-pattern

🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Object with `any` breaks `.with` chaining

gabrielalmeida opened this issue · comments

Describe the bug
Codegen generates type defs that includes any as object property types.
ts-pattern .with chaining breaks when trying to match against those objects.

// Sample type generated via codegen
type Rule = {
  // Replace any with other types and it works as expected
  unit: any;
};

const sample_rule: Rule = {
  unit: "day",
};

const result = match(sample_rule)
  .with({ unit: "day" }, () => "x")
  // Type is broken when chaining second .with, comment line below and it works as expected
  .with({ unit: "hour" }, () => "y")
  .otherwise(() => "w");
TSError: ⨯ Unable to compile TypeScript:
src/index.ts:16:11 - error TS2345: Argument of type '{ unit: string; }' is not assignable to parameter of type 'PatternMatcher<never>'.
  Object literal may only specify known properties, and 'unit' does not exist in type 'Matcher<never, unknown, any, any, unknown>'.

Code Sandbox with a minimal reproduction case
https://codesandbox.io/p/sandbox/misty-bird-wsckjn

Versions

  • TypeScript version: 5.2.2
  • ts-pattern version: 5.0.5
  • environment: node v16.17.0

v4.1.2 introduced "Make subsequent .with clause inherit narrowing from previous clauses" which seems to be the cause of this behavior. Reverting to a version prior than that works as expected.

That's unfortunately coming from this weird TypeScript behavior:

type X = Exclude<{ unit: any }, { readonly unit: 'day' }>;
  //   ^? never

I would expect X to be { unit: any } here, because { readonly unit: 'day' } is a small subset of { unit: any }...

I don't know if there is a fix for this that would be satisfactory in terms of type-checking performance (for example that wouldn't require walking through the input types to find anys and turn them into unknowns).