Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(deps): update dependency ts-pattern to v5 #344

Closed
wants to merge 1 commit into from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Dec 15, 2024

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
ts-pattern ^4.2.2 -> ^5.0.0 age adoption passing confidence

Release Notes

gvergnaud/ts-pattern (ts-pattern)

v5.6.0

Compare Source

This release contains two changes:

Typecheck pattern when using isMatching with 2 parameter.

It used to be possible to pass a pattern than could never match to isMatching. The new version checks that the provide pattern does match the value in second parameter:

type Pizza = { type: 'pizza'; topping: string };
type Sandwich = { type: 'sandwich'; condiments: string[] };
type Food = Pizza | Sandwich;

const fn = (food: Pizza | Sandwich) => {
    if (isMatching({ type: 'oops' }, food)) {
        //                  👆 used to type-check, now doesn't!
    }
}
Do not use P.infer as an inference point

When using P.infer<Pattern> to type a function argument, like in the following example:

const getWithDefault = <T extends P.Pattern>(
  input: unknown,
  pattern: T,
  defaultValue: P.infer<T> //  👈
): P.infer<T> =>
  isMatching(pattern, input) ? input : defaultValue

TypeScript could get confused and find type errors in the wrong spot:

const res = getWithDefault(null, { x: P.string }, 'oops') 
//                                     👆           👆 type error should be here
//                                 but it's here 😬

This new version fixes this problem.

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.5.0...v5.6.0

v5.5.0

Compare Source

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.4.0...v5.5.0

v5.4.0

Compare Source

The main thing — Faster type checking 🚀

This release brings a significant perf improvement to exhaustiveness checking, which led to a ~16% decrease in the time to type-check the full test suite of TS-Pattern:

Category Before After Evolution (%)
Instantiations 6,735,991 4,562,378 -32.33%
Memory used 732,233K 746,454K 1.95%
Assignability cache size 209,959 205,926 -1.92%
Identity cache size 28,093 28,250 0.56%
Check time 5.78s 4.83s -16.44%

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.3.1...v5.4.0

v5.3.1

Compare Source

Pattern-matching on symbol keys

Symbols used to be ignored in object patterns. They are now taken into account:

const symbolA = Symbol('symbol-a');
const symbolB = Symbol('symbol-b');

const obj = { [symbolA]: { [symbolB]: 'foo' } };
    
if (isMatching({ [symbolA]: { [symbolB]: 'bar' } }, obj)) {
   //  👆 Used to return true, now returns false!
   
   //  Since TS-Pattern wasn't reading symbols, this pattern used to be equivalent
   //  to the `{}` pattern that matches any value except null and undefined.
}

.exhaustive now throws a custom error

People have expressed the need to differentiate runtime errors that .exhaustive() might throw when the input is of an unexpected type from other runtime errors that could have happened in the same match expression. It's now possible with err instanceof NonExhaustiveError:

import { match, P, NonExhaustiveError }  from 'ts-pattern';

const fn = (input: string | number) => {
  return match(input)
    .with(P.string, () => "string!")
    .with(P.number, () => "number!")
    .exhaustive()
}

try {
  fn(null as string) // 👈 💥 
} catch (e) {
  if (e instanceof NonExhaustiveError) {
    // The input was invalid
  } else {
    // something else happened
  }
}

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.2.0...v5.3.1

v5.3.0

Compare Source

v5.2.0

Compare Source

The main thing

new P.string.length(n) pattern

P.string.length(len) matches strings with exactly len characters.

const fn = (input: string) =>
  match(input)
    .with(P.string.length(2), () => '🎉')
    .otherwise(() => '❌');

console.log(fn('ok')); // logs '🎉'

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.1.2...v5.2.0

v5.1.2

Compare Source

The main thing

When combining P.nonNullable and P.nullish, you should get an exhaustive pattern matching expression, but the following case was incorrectly considered non-exhaustive:

declare const input: {
  nested: string | number | null | undefined;
};

const res = match(input)
  .with({ nested: P.nonNullable }, (x) => {/* ... */})
  .with({ nested: P.nullish }, (x) => {/* ... */})
  // should type-check
  .exhaustive();

This is fixed now.

What's Changed
New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.1.1...v5.1.2

v5.1.1

Compare Source

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.1.0...v5.1.1

v5.1.0

Compare Source

New features
P.nonNullable wildcard

Add a new P.nonNullable pattern that will match any value except null or undefined.

import { match, P } from 'ts-pattern';

const input = null;

const output = match<number | null | undefined>(input)
  .with(P.nonNullable, () => 'it is a number!')
  .otherwise(() => 'it is either null or undefined!');

console.log(output);
// => 'it is either null or undefined!'

Closes #​60 #​154 #​190 and will be a work-around for #​143.

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.0.8...v5.1.0

v5.0.8

Compare Source

The main thing

This release includes type narrowing improvement to isMatching when used in its curried form:

type Pizza = { type: 'pizza', topping: string };
type Sandwich = { type: 'sandwich', condiments: string[] }
type Food =  Pizza | Sandwich;

declare const food: Food

const isPizza = isMatching({ type: 'pizza' })

if (isPizza(food)) {
  x  // Used to  infer `food` as `Food`, no infers `Pizza`
}

This also improves type checking performance for complex patterns and fixes a small bug in the ES5 build of TS-Pattern.

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.0.6...v5.0.8

v5.0.7

Compare Source

v5.0.6

Compare Source

Close issue issues

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.0.5...v5.0.6

v5.0.5

Compare Source

Bug fixes

The P module was mistakenly exposing some pattern methods that were intended to be namespaced by type. This release fixes this problem.

If you happened to use on of those following methods, here is where to find them now:

- P.between
+ P.number.between
- P.lt
+ P.number.lt
- P.gt
+ P.number.gt
- P.lte
+ P.number.lte
- P.gte
+ P.number.gte
- P.int
+ P.number.int
- P.finite
+ P.number.finite
- P.positive
+ P.number.positive
- P.negative
+ P.number.negative
- P.betweenBigInt
+ P.bigint.between
- P.ltBigInt
+ P.bigint.lt
- P.gtBigInt
+ P.bigint.gt
- P.lteBigInt
+ P.bigint.lte
- P.gteBigInt
+ P.bigint.gte
- P.positiveBigInt
+ P.bigint.positive
- P.negativeBigInt
+ P.bigint.negative

v5.0.4

Compare Source

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.0.3...v5.0.4

v5.0.3

Compare Source

What's Changed

Full Changelog: gvergnaud/ts-pattern@v5.0.2...v5.0.3

v5.0.2

Compare Source

What's Changed

New Contributors

Full Changelog: gvergnaud/ts-pattern@v5.0.0...v5.0.2

v5.0.1

Compare Source

v5.0.0: ❤️

Compare Source

TS-Pattern v5 is finally out ❤️

Breaking changes

.with is now evaluated eagerly

In the previous version of TS-Pattern, no code would execute until you called .exhaustive() or .otherwise(...). For example, in the following code block, nothing would be logged to the console or thrown:

// TS-Pattern v4
type Input = { type: 'ok'; value: number } | { type: 'error'; error: Error };

// We don't call `.exhaustive`, so handlers don't run.
function someFunction(input: Input) {
  match(input)
    .with({ type: 'ok' }, ({ value }) => {
      console.log(value);
    })
    .with({ type: 'error' }, ({ error }) => {
      throw error;
    });
}

someFunction({ type: 'ok', value: 42 }); // nothing happens

In TS-Pattern v5, however, the library will execute the matching handler as soon as it finds it:

// TS-Pattern v5
someFunction({ type: 'ok', value: 42 }); // logs "42" to the console!

Handlers are now evaluated eagerly instead of lazily. In practice, this shouldn't change anything as long as you always finish your pattern matching expressions by either .exhaustive or .otherwise.

Matching on Maps and Sets

Matching Set and Map instances using .with(new Set(...)) and .with(new Map(...)) is no longer supported. If you want to match specific sets and maps, you should now use the P.map(keyPattern, valuePattern) and P.set(valuePattern) patterns:

- import { match } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';

const someFunction = (value: Set<number> | Map<string, number>) =>
  match(value)
-   .with(new Set([P.number]), (set) => `a set of numbers`)
-   .with(new Map([['key', P.number]]), (map) => `map.get('key') is a number`)
+   .with(P.set(P.number), (set) => `a set of numbers`)
+   .with(P.map('key', P.number), (map) => `map.get('key') is a number`)
    .otherwise(() => null);
  • The subpattern we provide in P.set(subpattern) should match all values in the set.
  • The value subpattern we provide in P.map(keyPattern, subpattern) should only match the values matching keyPattern for the whole P.map(..) pattern to match the input.

New features

chainable methods

TS-Pattern v5's major addition is the ability to chain methods to narrow down the values matched by primitive patterns, like P.string or P.number.

Since a few examples is worth a thousand words, here are a few ways you can use chainable methods:

P.number methods
const example = (position: { x: number; y: number }) =>
  match(position)
    .with({ x: P.number.gte(100) }, (value) => '🎮')
    .with({ x: P.number.between(0, 100) }, (value) => '🎮')
    .with(
      {
        x: P.number.positive().int(),
        y: P.number.positive().int(),
      },
      (value) => '🎮'
    )
    .otherwise(() => 'x or y is negative');

Here is the full list of number methods:

  • P.number.between(min, max): matches numbers between min and max.
  • P.number.lt(max): matches numbers smaller than max.
  • P.number.gt(min): matches numbers greater than min.
  • P.number.lte(max): matches numbers smaller than or equal to max.
  • P.number.gte(min): matches numbers greater than or equal to min.
  • P.number.int(): matches integers.
  • P.number.finite(): matches all numbers except Infinity and -Infinity
  • P.number.positive(): matches positive numbers.
  • P.number.negative(): matches negative numbers.
P.string methods
const example = (query: string) =>
  match(query)
    .with(P.string.startsWith('SELECT'), (query) => `selection`)
    .with(P.string.endsWith('FROM user'), (query) => `👯‍♂️`)
    .with(P.string.includes('*'), () => 'contains a star')
    // Methods can be chained:
    .with(P.string.startsWith('SET').includes('*'), (query) => `🤯`)
    .exhaustive();

Here is the full list of string methods:

  • P.string.startsWith(str): matches strings that start with str.
  • P.string.endsWith(str): matches strings that end with str.
  • P.string.minLength(min): matches strings with at least min characters.
  • P.string.maxLength(max): matches strings with at most max characters.
  • P.string.includes(str): matches strings that contain str.
  • P.string.regex(RegExp): matches strings if they match this regular expression.
Global methods

Some methods are available for all primitive type patterns:

  • P.{..}.optional(): matches even if this property isn't present on the input object.
  • P.{..}.select(): injects the matched value into the handler function.
  • P.{..}.and(pattern): matches if the current pattern and the provided pattern match.
  • P.{..}.or(pattern): matches if either the current pattern or the provided pattern match.
const example = (value: unknown) =>
  match(value)
    .with(
      {
        username: P.string,
        displayName: P.string.optional(),
      },
      () => `{ username:string, displayName?: string }`
    )
    .with(
      {
        title: P.string,
        author: { username: P.string.select() },
      },
      (username) => `author.username is ${username}`
    )
    .with(
      P.instanceOf(Error).and({ source: P.string }),
      () => `Error & { source: string }`
    )
    .with(P.string.or(P.number), () => `string | number`)
    .otherwise(() => null);

Variadic tuple patterns

With TS-Pattern, you are now able to create array (or more accurately tuple) pattern with a variable number of elements:

const example = (value: unknown) =>
  match(value)
    .with(
      // non-empty list of strings
      [P.string, ...P.array(P.string)],
      (value) => `value: [string, ...string[]]`
    )
    .otherwise(() => null);

Array patterns that include a ...P.array are called variadic tuple patterns. You may only have a single ...P.array, but as many fixed-index patterns as you want:

const example = (value: unknown) =>
  match(value)
    .with(
      [P.string, P.string, P.string, ...P.array(P.string)],
      (value) => `value: [string, string, string, ...string[]]`
    )
    .with(
      [P.string, P.string, ...P.array(P.string)],
      (value) => `value: [string, string, ...string[]]`
    )
    .with([], (value) => `value: []`)
    .otherwise(() => null);

Fixed-index patterns can also be set after the ...P.array variadic, or on both sides!

const example = (value: unknown) =>
  match(value)
    .with(
      [...P.array(P.number), P.string, P.number],
      (value) => `value: [...number[], string, number]`
    )
    .with(
      [P.boolean, ...P.array(P.string), P.number, P.symbol],
      (value) => `value: [boolean, ...string[], number, symbol]`
    )
    .otherwise(() => null);

Lastly, argument of P.array is now optional, and will default to P._, which matches anything:

const example = (value: unknown) =>
  match(value)
    //                         👇
    .with([P.string, ...P.array()], (value) => `value: [string, ...unknown[]]`)
    .otherwise(() => null);

.returnType

In TS-Pattern v4, the only way to explicitly set the return type of your match expression is to set the two <Input, Output> type parameters of match:

// TS-Pattern v4
match<
  { isAdmin: boolean; plan: 'free' | 'paid' }, // input type
  number // return type
>({ isAdmin, plan })
  .with({ isAdmin: true }, () => 123)
  .with({ plan: 'free' }, () => 'Oops!');
//                              ~~~~~~ ❌ not a number.

the main drawback is that you need to set the input type explicitly too, even though TypeScript should be able to infer it.

In TS-Pattern v5, you can use the .returnType<Type>() method to only set the return type:

match({ isAdmin, plan })
  .returnType<number>() // 👈 new
  .with({ isAdmin: true }, () => 123)
  .with({ plan: 'free' }, () => 'Oops!');
//                              ~~~~~~ ❌ not a number.

What's Changed

Full Changelog: gvergnaud/ts-pattern@v4.3.0...v5.0.0


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

Copy link

vercel bot commented Dec 15, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
safeql ⬜️ Ignored (Inspect) Visit Preview Dec 15, 2024 10:31pm

Copy link

changeset-bot bot commented Dec 15, 2024

⚠️ No Changeset found

Latest commit: 9e4973b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Newbie012 Newbie012 enabled auto-merge (squash) December 16, 2024 10:01
@Newbie012 Newbie012 closed this Dec 16, 2024
auto-merge was automatically disabled December 16, 2024 10:02

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant