gvergnaud/ts-pattern (ts-pattern)
### [`v4.3.0`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.3.0)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/788c082e85c3e03374177b253f002fc11c38d059...v4.3.0)
#### TS-Pattern and node16
TS-Pattern now fully supports `moduleResolution: node16`, with both ES and CommonJS modules. This resolves the long standing issue number [#110](https://redirect.github.com/gvergnaud/ts-pattern/issues/110). Special thanks to [@Andarist](https://redirect.github.com/Andarist) and [@frankie303](https://redirect.github.com/frankie303) for helping me understand and fix this issue ❤️
#### What's Changed
- build: better support moduleResolution: bundler by [@gvergnaud](https://redirect.github.com/gvergnaud) in [https://github.com/gvergnaud/ts-pattern/pull/158](https://redirect.github.com/gvergnaud/ts-pattern/pull/158)
- Gvergnaud/fix build for nodenext and commonjs by [@gvergnaud](https://redirect.github.com/gvergnaud) in [https://github.com/gvergnaud/ts-pattern/pull/160](https://redirect.github.com/gvergnaud/ts-pattern/pull/160)
**Full Changelog**: https://github.com/gvergnaud/ts-pattern/compare/v4.2.2...v4.3.0
### [`v4.2.3`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.2...788c082e85c3e03374177b253f002fc11c38d059)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.2...788c082e85c3e03374177b253f002fc11c38d059)
### [`v4.2.2`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.2)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.1...v4.2.2)
##### Bug fixes:
- Issue [#142](https://redirect.github.com/gvergnaud/ts-pattern/issues/142): Fixes a type inference bug when the input type only has optional properties. [commit 3c36992](https://redirect.github.com/gvergnaud/ts-pattern/commit/3c36992)
### [`v4.2.1`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.1)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.0...v4.2.1)
#### Bug fixes
This release fixes inference of `P.array` when the input is a readonly array (issue [#148](https://redirect.github.com/gvergnaud/ts-pattern/issues/148))
```ts
declare const input: readonly {
readonly title: string;
readonly content: string;
}[];
const output = match(input)
.with(
P.array({ title: P.string, content: P.string }),
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Used to error, now works
(posts) => 'a list of posts!'
)
.otherwise(() => 'something else');
```
### [`v4.2.0`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.0)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.4...v4.2.0)
#### Features
##### Better inference for `match` and `.with`
##### match
When using match with an inline array, it will now infer its type as tuple automatically, even when not using `as const`. This means that exhaustiveness checking will also improve in this case:
```ts
function f(a: boolean, b: boolean) {
// infered as `[boolean, boolean]`
return (
match([a, b])
// we can pattern match on all cases
.with([true, true], () => false)
.with([false, true], () => true)
.with([true, false], () => true)
.with([false, false], () => false)
// ✅ Failed in TS-pattern v4.1 but works in v4.2!
.exhaustive()
);
}
```
##### `.with(...)`
Thanks to the help of [@Andarist](https://redirect.github.com/Andarist), this release fixes a long-standing issue of `.with`.
Until now, patterns like `P.array`, `P.union` or `P.when` didn't have proper type inference when used in `.with()` directly. Here are a few behaviors that use to be incorrect and work now:
```ts
match<'a' | 'b'>('a')
.with(P.union('this is wrong'), x => x)
// ~~~~~~~~~~~~~~~
// ❌ no longer type-check in v4.2
.otherwise(x => x)
match<'a' | 'b'>('a')
.with(P.array(123), x => x)
// ~~~
// ❌ no longer type-check in v4.2
.otherwise(x => x)
match<'a' | 'b'>('a')
.with(P.when((x) => true), x => x)
// 👆
// used to be of type `unknown`, now `'a' | 'b'`
.otherwise(x => x)
```
This also fixes the following issue: [https://github.com/gvergnaud/ts-pattern/issues/140](https://redirect.github.com/gvergnaud/ts-pattern/issues/140)
### [`v4.1.4`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.1.4)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.3...v4.1.4)
#### Bug fixes
##### Issue [https://github.com/gvergnaud/ts-pattern/issues/138](https://redirect.github.com/gvergnaud/ts-pattern/issues/138) — inference issues with `P.not`
When using `P.not` with a literal value like `P.not(2)`, Exhaustive checking was mistakenly considering that all numbers had been handled, even though `2` isn't. This was causing this code to erroneously type-check:
```ts
match(input)
.with(P.not(10), () => 'not video of 10 seconds.')
.exhaustive() // This type-checked even though the value `10` isn't handled
```
This new patch version fixes this bug.
##### Caveats
Exhaustive pattern-matching expressions where the input is a primitive (like `number`) and the pattern is a negation of a literal number (like `P.not(2)`) are no longer considered exhaustive:
```ts
match(1)
.with(P.not(2), () => 'not 2')
.with(2, () => '2')
.exhaustive(); // ❌ `number` isn't handled
```
Technically, this expression is exhaustive but there is no easy way to type-check it is without negated types ([https://github.com/microsoft/TypeScript/pull/29317](https://redirect.github.com/microsoft/TypeScript/pull/29317)), so this is an expected false-positive for now.
Exhaustive checking works as expected when the pattern **and** the input are primitive types:
```ts
match(1)
.with(P.not(P.number), () => 'not 2')
.with(P.number, () => '2')
.exhaustive(); // ✅
```
And when the pattern and the input **are** literal types:
```ts
match<1 | 2>(1)
.with(P.not(2), () => '1')
.with(2, () => '2')
.exhaustive(); // ✅
```
### [`v4.1.3`](https://redirect.github.com/gvergnaud/ts-pattern/compare/9116af6ee255550ea7c8ba0ffe986a09a887fd92...2aa12fa7c5aa1dfcbac7bb547453166b6b8f7c26)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.2...v4.1.3)
### [`v4.1.2`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.1.2)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/53b4582aa8218be84901497ba75d494d90c7c138...v4.1.2)
### Make subsequent `.with` clause inherit narrowing from previous clauses
#### Problem
With the current version of ts-pattern, nothing prevents you from writing `.with` clauses that will never match any input because the case has already been handled in a previous close:
```ts
type Plan = 'free' | 'pro' | 'premium';
const welcome = (plan: Plan) =>
match(plan)
.with('free', () => 'Hello free user!')
.with('pro', () => 'Hello pro user!')
.with('pro', () => 'Hello awesome user!')
// 👆 This will never match!
// We should exclude "pro"
// from the input type to
// reject duplicated with clauses.
.with('premium', () => 'Hello premium user!')
.exhaustive()
```
#### Approach
Initially, I was reluctant to narrow the input type on every call of `.with` because of type checking performance. TS-Pattern's exhaustive checking is pretty expensive because it not only narrows top-level union types, but also nested ones. In order to make that work, TS-Pattern needs to **distribute** nested union types when they are matched by a pattern, which can sometimes generate large unions which are more expensive to match.
I ended up settling on a more modest approach, which turns out to have great performance: Only narrowing top level union types. This should cover 80% of cases, including the aforementioned one:
```ts
type Plan = 'free' | 'pro' | 'premium';
const welcome = (plan: Plan) =>
match(plan)
.with('free', () => 'Hello free user!')
.with('pro', () => 'Hello pro user!')
.with('pro', () => 'Hello awesome user!')
// ^ ❌ Does not type-check in TS-Pattern v4.1!
.with('premium', () => 'Hello premium user!')
.exhaustive()
```
#### Examples of invalid cases that no longer type check:
Narrowing will work on unions of literals, but also discriminated unions of objects:
```ts
type Entity =
| { type: 'user', name: string }
| { type: 'org', id: string };
const f = (entity: Entity) =>
match(entity)
.with({ type: 'user' }, () => 'user!')
.with({ type: 'user' }, () => 'user!')
// ^ ❌ Does not type-check in TS-Pattern v4.1!
.with({ type: 'org' }, () => 'org!')
.exhaustive()
```
It also works with tuples, and any other union of data structures:
```ts
type Entity =
| [type: 'user', name: string]
| [type: 'org', id: string]
const f = (entity: Entity) =>
match(entity)
.with(['user', P.any], () => 'user!')
.with(['user', P.any], () => 'user!')
// ^ ❌ Does not type-check in TS-Pattern v4.1!
.with(['org', P.any], () => 'org!')
.exhaustive()
```
It works with any patterns, including wildcards:
```ts
type Entity =
| [type: 'user', name: string]
| [type: 'org', id: string]
const f = (entity: Entity) =>
match(entity)
.with(P.any, () => 'user!') // catch all
.with(['user', P.any], () => 'user!')
// ^ ❌ Does not type-check in TS-Pattern v4.1!
.with(['org', P.any], () => 'org!')
// ^ ❌ Does not type-check in TS-Pattern v4.1!
.exhaustive()
```
#### Examples of invalid cases that *still* type check:
This won't prevent you from writing duplicated clauses in case the union you're matching is nested:
```ts
type Plan = 'free' | 'pro' | 'premium';
type Role = 'viewer' | 'contributor' | 'admin';
const f = (plan: Plan, role: Role) =>
match([plan, role] as const)
.with(['free', 'admin'], () => 'free admin')
.with(['pro', P.any], () => 'all pros')
.with(['pro', 'admin'], () => 'admin pro')
// ^ this unfortunately still type-checks
.otherwise(() => 'other users!')
```
#### `.otherwise`'s input also inherit narrowing
The nice effect of refining the input value on every `.with` clause is that `.otherwise` also get a narrowed input type:
```ts
type Plan = 'free' | 'pro' | 'premium';
const welcome = (plan: Plan) =>
match(plan)
.with('free', () => 'Hello free user!')
.otherwise((input) => 'pro or premium')
// 👆 input is inferred as `'pro' | 'premium'`
```
#### Perf
Type-checking performance is generally better, with a 29% reduction of type instantiation and a 17% check time improvement on my benchmark:
| description | before | after | delta |
| ------------------------- | ------- | ------- | ------- |
| Files | 181 | 181 | 0% |
| Lines of Library | 28073 | 28073 | 0% |
| Lines of Definitions | 49440 | 49440 | 0% |
| Lines of TypeScript | 11448 | 11516 | 0.59% |
| Nodes of Library | 119644 | 119644 | 0% |
| Nodes of Definitions | 192409 | 192409 | 0% |
| Nodes of TypeScript | 57791 | 58151 | 0.62% |
| Identifiers | 120063 | 120163 | 0.08% |
| Symbols | 746269 | 571935 | -23.36% |
| Types | 395519 | 333052 | -15.79% |
| Instantiations | [`3810512`](https://redirect.github.com/gvergnaud/ts-pattern/commit/3810512) | [`2670937`](https://redirect.github.com/gvergnaud/ts-pattern/commit/2670937) | -29.90% |
| Memory used | 718758K | 600076K | -16.51% |
| Assignability cache size | 339114 | 311641 | -8.10% |
| Identity cache size | 17071 | 17036 | -0.20% |
| Subtype cache size | 2759 | 2739 | -0.72% |
| Strict subtype cache size | 2544 | 1981 | -22.13% |
| I/O Read time | 0.01s | 0.01s | 0% |
| Parse time | 0.28s | 0.28s | 0% |
| ResolveModule time | 0.01s | 0.02s | 100% |
| ResolveTypeReference time | 0.01s | 0.01s | 0% |
| Program time | 0.34s | 0.34s | 0% |
| Bind time | 0.13s | 0.14s | 7.69% |
| Check time | 5.28s | 4.37s | -17.23% |
| Total time | 5.75s | 4.85s | -15.65% |
#### Other changes
- TS-Pattern's `package.json` exports have been updated to provide a default export for build systems that read neither `import` nor `require`.
### [`v4.1.1`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.0...53b4582aa8218be84901497ba75d494d90c7c138)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.0...53b4582aa8218be84901497ba75d494d90c7c138)
### [`v4.1.0`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.6...v4.1.0)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.6...v4.1.0)
### [`v4.0.6`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.0.6)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.5...v4.0.6)
##### Bug fixes
- Update `P.instanceOf` to accept not only classes but also abstract classes. [Related issue](https://redirect.github.com/gvergnaud/ts-pattern/issues/120), https://github.com/gvergnaud/ts-pattern/commit/000927ca441e58403a8495ddb49eb47828f5d1bb https://github.com/gvergnaud/ts-pattern/commit/ebeb39ba9956596602c34fd5e067db164b70f805
```ts
abstract class A {}
class B extends A {}
class C extends A {}
const object = new B() as B | C;
match(object)
.with(P.instanceOf(A), a => ...) // a: B | C
// ^
// ✅ This type-checks now!
.exhaustive()
```
### [`v4.0.5`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.0.5)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.4...v4.0.5)
This release adds the ./package.json file to [exported files](https://nodejs.org/api/packages.html#subpath-exports) ([PR](https://redirect.github.com/gvergnaud/ts-pattern/pull/97) by [@zoontek](https://redirect.github.com/zoontek)).
> This fixes [https://github.com/nodejs/node/issues/33460](https://redirect.github.com/nodejs/node/issues/33460)
> Without it, it breaks require.resolve, used by a lot of tooling (Rollup, React native CLI, etc)
### [`v4.0.4`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.0.4)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/23faa16032d4bb918ac92a1891e1477eb9425c8d...v4.0.4)
Fixes:
- When nesting `P.array()` and `P.select()`, the handler function used to receive `undefined` instead of an empty array when the input array was empty. Now it received an empty array as expected:
```ts
match([])
.with(P.array({ name: P.select() }), (names) => names) /* names has type `string[]` and value `[]` */
// ...
.exhaustive()
```
- The types used to forbid using an empty array pattern (`[]`) when matching on a value of type `unknown`. This has been fixed.
```ts
const f = (x: unknown) => match(x).with([], () => "this is an empty array!").otherwise(() => "?")
```
Commits:
- https://github.com/gvergnaud/ts-pattern/commit/54a39c03dd35f150d1da321e039b94b98c9d01dd
- https://github.com/gvergnaud/ts-pattern/commit/33567840ed5e5809c2df85b9e7df5c996910280f
### [`v4.0.3`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.2...23faa16032d4bb918ac92a1891e1477eb9425c8d)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.2...23faa16032d4bb918ac92a1891e1477eb9425c8d)
### [`v4.0.2`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.0.2)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.0.1...v4.0.2)
Patch release containing a few runtime performance improvements:
- Use a `Builder` class internally in match expression to rely on prototypal inheritance instead of defining method every time `.with` is called.
- Handle the `.with(pattern, handler)` case separately from `.with(...pattern, handler)` to avoid iterating on params and make it faster.
### [`v4.0.1`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.0.1): ✨ v4.0.1 ✨
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.3.5...v4.0.1)
#### ⚠️ Breaking changes
##### Imports
type-specific wildcard patterns have moved from `__.` to a new `Pattern` qualified module, also exported as `P` by ts-pattern.
```diff
- import { match, __ } from 'ts-pattern';
+ import { match, Pattern } from 'ts-pattern';
const toString = (value: string | number) =>
match(value)
- .with(__.string, (v) => v)
- .with(__.number, (v) => `${v}`)
+ .with(Pattern.string, (v) => v)
+ .with(Pattern.number, (v) => `${v}`)
.exhaustive();
```
or
```diff
- import { match, __ } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';
const toString = (value: string | number) =>
match(value)
- .with(__.string, (v) => v)
- .with(__.number, (v) => `${v}`)
+ .with(P.string, (v) => v)
+ .with(P.number, (v) => `${v}`)
.exhaustive();
```
##### `__`
The top level `__` export was moved to `P._` and `P.any`:
```diff
- import { match, __ } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';
const toString = (value: string | number) =>
match(value)
- .with(__, (v) => `${v}`)
+ .with(P._, (v) => `${v}`)
// OR
+ .with(P.any, (v) => `${v}`)
.exhaustive();
```
##### `select()`, `not()`, `when()`
Function to create patterns have been moved to the `P` module.
```diff
- import { match, select, not, when } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';
const toString = (value: number) =>
match(value)
- .with({ prop: select() }, (v) => `${v}`)
+ .with({ prop: P.select() }, (v) => `${v}`)
- .with({ prop: not(10) }, (v) => `${v}`)
+ .with({ prop: P.not(10) }, (v) => `${v}`)
- .with({ prop: when((x) => x < 5) }, (v) => `${v}`)
+ .with({ prop: P.when((x) => x < 5) }, (v) => `${v}`)
.exhaustive();
```
##### `Pattern` type
the `Pattern` type which used to be exported at the toplevel is now accessible at `P.Pattern`.
```diff
- import { match, Pattern } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';
- const pattern: Pattern = P.when(x => x > 2);
+ const pattern: P.Pattern = P.when(x => x > 2);
```
##### list patterns
The syntax for matching on a list of elements with an unknown length has changed from `[subpattern]` to `P.array(subpattern)`.
Example:
```diff
- import { match, __ } from 'ts-pattern';
+ import { match, P } from 'ts-pattern';
const parseUsers = (response: unknown) =>
match(response)
- .with({ data: [{ name: __.string }] }, (users) => users)
+ .with({ data: P.array({ name: P.string }) }, (users) => users)
.otherwise(() => []);
```
Now `[subpattern]` matches arrays with 1 element in them. This is more consistent with native language features, like destructuring assignement and is overall more intuitive. This will resolve [#69](https://redirect.github.com/gvergnaud/ts-pattern/issues/69), [#62](https://redirect.github.com/gvergnaud/ts-pattern/issues/62) and [#46](https://redirect.github.com/gvergnaud/ts-pattern/issues/46).
##### NaN
The `__.NaN` pattern has been replaced by simply using the NaN value in the pattern:
```diff
match(NaN)
- .with(__.NaN, () => "this is not a number")
+ .with(NaN, () => "this is not a number")
.otherwise((n) => n);
```
#### ⭐️ New features ⭐️
Here is the list of all new features which have been added in TS-Pattern v4.
##### Arrays and unary tuples
##### `P.array(pattern)`
To match an array of elements, you can now use `P.array`:
```ts
import { match, P } from 'ts-pattern';
const responsePattern = {
data: P.array({
id: P.string,
post: P.array({
title: P.string,
content: P.string,
}),
}),
};
fetchSomething().then((value: unknown) =>
match(value)
.with(responsePattern, (value) => {
// value: { data: { id: string, post: { title: string, content: string }[] }[] }
return value;
})
.otherwise(() => {
throw new Error('unexpected response');
})
);
```
##### Optional object properties
##### `P.optional(pattern)`
If you want one of the keys of your pattern to be optional, you can now use `P.optional(subpattern)`.
If you `P.select()` something in an optional pattern, it's type will be infered as `T | undefined`.
```ts
import { match, P } from 'ts-pattern';
const doSomethingWithUser = (user: User | Org) =>
match(user)
.with(
{
type: 'user',
detail: {
bio: P.optional(P.string),
socialLinks: P.optional({
twitter: P.select(),
}),
},
},
(twitterLink, value) => {
// twitterLink: string | undefined
/**
* value.detail: {
* bio?: string,
* socialLinks?: {
* twitter: string
* }
* }
**/
}
)
.otherwise(() => {
throw new Error('unexpected response');
});
```
##### Union & intersection patterns
`P.union(...patterns)` and `P.intersection(...patterns)` combine several patterns into a single one, either by checking that one of them match the input (`p.union`) or all of them match it (`P.intersection`).
##### `P.union(...patterns)`
```ts
type Input =
| { type: 'a'; value: string }
| { type: 'b'; value: number }
| {
type: 'c';
value:
| { type: 'd'; value: boolean }
| { type: 'e'; value: string[] }
| { type: 'f'; value: number[] };
};
const f = (input: Input) =>
match(input)
.with(
{ type: P.union('a', 'b') },
// x: { type: 'a'; value: string } | { type: 'b'; value: number }
(x) => 'branch 1'
)
.with(
// P.union can take any subpattern:
{
type: 'c',
value: { value: P.union(P.boolean, P.array(P.string)) },
},
(x) => 'branch 2' // x.value.value: boolean | string[]
)
.with({ type: 'c', value: { type: 'f' } }, () => 'branch 3')
.exhaustive();
```
##### `P.intersection(...patterns)`
```ts
class A {
constructor(public foo: 'bar' | 'baz') {}
}
class B {
constructor(public str: string) {}
}
const f = (input: { prop: A | B }) =>
match(input)
.with(
{ prop: P.intersection(P.instanceOf(A), { foo: 'bar' }) },
// prop: A & { foo: 'bar' }
({ prop }) => 'branch 1'
)
.with(
{ prop: P.intersection(P.instanceOf(A), { foo: 'baz' }) },
// prop: A & { foo: 'baz' }
({ prop }) => 'branch 2'
)
.with(
{ prop: P.instanceOf(B) },
// prop: B
({ prop }) => 'branch 3'
)
.exhaustive();
```
##### Select with sub pattern
`P.select()` now can take a subpattern and match only what the subpattern matches:
```ts
type Img = { type: 'img'; src: string };
type Text = { type: 'text'; content: string; length: number };
type User = { type: 'user'; username: string };
type Org = { type: 'org'; orgId: number };
const post = (input: { author: User | Org; content: Text | Img }) =>
match(input)
.with(
{ author: P.select({ type: 'user' }) },
// user: User
(user) => {}
)
.with(
{
// This also works with named selections
author: P.select('org', { type: 'org' }),
content: P.select('text', { type: 'text' }),
},
// org: Org, text: Text
({ org, text }) => {}
)
.otherwise(() => {
// ...
});
```
##### Infer the matching types from a pattern
##### `P.infer`
TS-Pattern is pretty handy for parsing unknown payloads like HTTP responses. You can write a pattern for the shape you are expecting, and then use `isMatching(pattern, response)` to make sure the response has the correct shape.
One limitation TS-Pattern had in its previous version was that it did not provide a way to get the TypeScript type of the value a given pattern matches. This is what `P.infer` does :)
```ts
const postPattern = {
title: P.string,
description: P.optional(P.string),
content: P.string,
likeCount: P.number,
};
type Post = P.infer;
// Post: { title: string, description?: string, content: string, likeCount: number }
const userPattern = {
name: P.string,
postCount: P.number,
bio: P.optional(P.string),
posts: P.optional(P.array(postPattern)),
};
type User = P.infer;
// User: { name: string, postCount: number, bio?: string, posts?: Post[] }
const isUserList = isMatching(P.array(userPattern));
const res = await fetchUsers();
if (isUserList(res)) {
// res: User
}
```
##### New type specific wildcards
##### `P.symbol`
`P.symbol` is a wildcard pattern matching any **symbol**.
```ts
match(Symbol('Hello'))
.with(P.symbol, () => 'this is a symbol!')
.exhaustive();
```
##### `P.bigint`
`P.bigint` is a wildcard pattern matching any **bigint**.
```ts
match(200n)
.with(P.bigint, () => 'this is a bigint!')
.exhaustive();
```
### [`v3.3.5`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v3.3.5)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.3.4...v3.3.5)
#### Bug fixes
This fixes a type inference bug impacting handler functions with explicit type annotations.
It used to be possible to annotate the handler function with an invalid type annotation. Thanks to this commit, it no longer type-checks https://github.com/gvergnaud/ts-pattern/commit/2d750742d95d38e4cab5ff1f2915050f2081441a.
See the related issue for more details: [#73](https://redirect.github.com/gvergnaud/ts-pattern/issues/73)
### [`v3.3.4`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v3.3.4)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/407e62e2ae53a13c70d7cfd153b1e52d1604630e...v3.3.4)
#### Bug fixes
This release fixes a type inference bug specific to Error sub classes. See the related issue for more details: [https://github.com/gvergnaud/ts-pattern/issues/63](https://redirect.github.com/gvergnaud/ts-pattern/issues/63)
### [`v3.3.3`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.3.2...407e62e2ae53a13c70d7cfd153b1e52d1604630e)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.3.2...407e62e2ae53a13c70d7cfd153b1e52d1604630e)
### [`v3.3.2`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v3.3.2)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.3.1...v3.3.2)
This patch contains some compile time perf improvements.
[@ahejlsberg](https://redirect.github.com/ahejlsberg) recently implemented tail call elimination for recursive conditional types ([https://github.com/microsoft/TypeScript/pull/45711](https://redirect.github.com/microsoft/TypeScript/pull/45711)). This release is preparation work to take advantage of this new feature by making most type helper functions tail recursive. From the non scientific tests I made on my machine, this also improves the compilation time of the `tests/` folder quite significantly on our current TS version (4.4). Compilation is ~ 20% faster.
### [`v3.3.1`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v3.3.1)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.2.5...v3.3.1)
#### Features
Add a `__.NaN` pattern, matching only `NaN`s values. Thanks [@mhintz](https://redirect.github.com/mhintz) for adding this
```ts
const res = match(NaN)
.with(null, () => 'null!')
.with(__.NaN, () => 'NaN!')
.with(__.number, (x) => 'a number!')
.exhaustive();
console.log(res)
// => 'NaN!'
```
#### Bugfix
Update the `__.number` pattern to also match on `NaN` values.
Since `NaN` has type `number` in TypeScript, there is no way to distinguish a `NaN` from a regular number at the type level. This was causing an issue where `.exhaustive()` considered all numbers handled by the `__.number` pattern even though `NaN` wasn't matched by it, resulting in possible runtime errors.
```ts
const res = match(NaN)
.with(null, () => 'null!')
.with(__.number, (x) => 'a number!')
// This used to throw at runtime because NaN wasn't matched by __.number
.exhaustive();
console.log(res)
// => 'a number!'
```
### [`v3.2.5`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v3.2.5)
[Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v3.2.4...v3.2.5)
##### Bugfixes
- Fix a bug with exhaustiveness checking, where a case could wrongfully be considered handled even if the pattern was containing additional keys that aren't present in the input type. This can happen when making a typo when declaring your pattern for instance, as demonstrated in the issue [#44](https://redirect.github.com/gvergnaud/ts-pattern/issues/44) .
```ts
type Person = {
sex: "Male" | "Female";
age: "Adult" | "Child";
};
function summary(person: Person): string {
return (
match(person)
// Typo – "agf" should be "age"
.with({ sex: "Female", agf: "Adult" }, () => "Woman")
.with({ sex: "Female", age: "Child" }, () => "Girl")
.with({ sex: "Male", age: "Adult" }, () => "Man")
.with({ sex: "Male", age: "Child" }, () => "Boy")
// Bugfix: This pattern is no longer considered exhaustive!
.exhaustive()
);
}
```
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 becomes conflicted, 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 contains the following updates:
4.2.1
->4.3.0
Release Notes
gvergnaud/ts-pattern (ts-pattern)
### [`v4.3.0`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.3.0) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/788c082e85c3e03374177b253f002fc11c38d059...v4.3.0) #### TS-Pattern and node16 TS-Pattern now fully supports `moduleResolution: node16`, with both ES and CommonJS modules. This resolves the long standing issue number [#110](https://redirect.github.com/gvergnaud/ts-pattern/issues/110). Special thanks to [@Andarist](https://redirect.github.com/Andarist) and [@frankie303](https://redirect.github.com/frankie303) for helping me understand and fix this issue ❤️ #### What's Changed - build: better support moduleResolution: bundler by [@gvergnaud](https://redirect.github.com/gvergnaud) in [https://github.com/gvergnaud/ts-pattern/pull/158](https://redirect.github.com/gvergnaud/ts-pattern/pull/158) - Gvergnaud/fix build for nodenext and commonjs by [@gvergnaud](https://redirect.github.com/gvergnaud) in [https://github.com/gvergnaud/ts-pattern/pull/160](https://redirect.github.com/gvergnaud/ts-pattern/pull/160) **Full Changelog**: https://github.com/gvergnaud/ts-pattern/compare/v4.2.2...v4.3.0 ### [`v4.2.3`](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.2...788c082e85c3e03374177b253f002fc11c38d059) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.2...788c082e85c3e03374177b253f002fc11c38d059) ### [`v4.2.2`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.2) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.1...v4.2.2) ##### Bug fixes: - Issue [#142](https://redirect.github.com/gvergnaud/ts-pattern/issues/142): Fixes a type inference bug when the input type only has optional properties. [commit 3c36992](https://redirect.github.com/gvergnaud/ts-pattern/commit/3c36992) ### [`v4.2.1`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.1) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.2.0...v4.2.1) #### Bug fixes This release fixes inference of `P.array` when the input is a readonly array (issue [#148](https://redirect.github.com/gvergnaud/ts-pattern/issues/148)) ```ts declare const input: readonly { readonly title: string; readonly content: string; }[]; const output = match(input) .with( P.array({ title: P.string, content: P.string }), // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Used to error, now works (posts) => 'a list of posts!' ) .otherwise(() => 'something else'); ``` ### [`v4.2.0`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.2.0) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.4...v4.2.0) #### Features ##### Better inference for `match` and `.with` ##### match When using match with an inline array, it will now infer its type as tuple automatically, even when not using `as const`. This means that exhaustiveness checking will also improve in this case: ```ts function f(a: boolean, b: boolean) { // infered as `[boolean, boolean]` return ( match([a, b]) // we can pattern match on all cases .with([true, true], () => false) .with([false, true], () => true) .with([true, false], () => true) .with([false, false], () => false) // ✅ Failed in TS-pattern v4.1 but works in v4.2! .exhaustive() ); } ``` ##### `.with(...)` Thanks to the help of [@Andarist](https://redirect.github.com/Andarist), this release fixes a long-standing issue of `.with`. Until now, patterns like `P.array`, `P.union` or `P.when` didn't have proper type inference when used in `.with()` directly. Here are a few behaviors that use to be incorrect and work now: ```ts match<'a' | 'b'>('a') .with(P.union('this is wrong'), x => x) // ~~~~~~~~~~~~~~~ // ❌ no longer type-check in v4.2 .otherwise(x => x) match<'a' | 'b'>('a') .with(P.array(123), x => x) // ~~~ // ❌ no longer type-check in v4.2 .otherwise(x => x) match<'a' | 'b'>('a') .with(P.when((x) => true), x => x) // 👆 // used to be of type `unknown`, now `'a' | 'b'` .otherwise(x => x) ``` This also fixes the following issue: [https://github.com/gvergnaud/ts-pattern/issues/140](https://redirect.github.com/gvergnaud/ts-pattern/issues/140) ### [`v4.1.4`](https://redirect.github.com/gvergnaud/ts-pattern/releases/tag/v4.1.4) [Compare Source](https://redirect.github.com/gvergnaud/ts-pattern/compare/v4.1.3...v4.1.4) #### Bug fixes ##### Issue [https://github.com/gvergnaud/ts-pattern/issues/138](https://redirect.github.com/gvergnaud/ts-pattern/issues/138) — inference issues with `P.not` When using `P.not` with a literal value like `P.not(2)`, Exhaustive checking was mistakenly considering that all numbers had been handled, even though `2` isn't. This was causing this code to erroneously type-check: ```ts matchConfiguration
📅 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 becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.