Add TaggedUnion type (#566)

This commit is contained in:
Pyry Heiskanen 2023-03-13 09:05:24 +02:00 committed by GitHub
parent 9b12767a2f
commit 2e1cec8aaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 0 deletions

1
index.d.ts vendored
View File

@ -7,6 +7,7 @@ export * from './source/observable-like';
// Utilities
export type {EmptyObject, IsEmptyObject} from './source/empty-object';
export type {Except} from './source/except';
export type {TaggedUnion} from './source/tagged-union';
export type {Writable} from './source/writable';
export type {WritableDeep} from './source/writable-deep';
export type {Merge} from './source/merge';

View File

@ -178,6 +178,7 @@ Click the type names for complete docs.
- [`IsNumericLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is a `number` or `bigint` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
- [`IsBooleanLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is a `true` or `false` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
- [`IsSymbolLiteral`](source/is-literal.d.ts) - Returns a boolean for whether the given type is a `symbol` [literal type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types).
- [`TaggedUnion`](source/tagged-union.d.ts) - Create a union of types that share a common discriminant property.
### JSON

51
source/tagged-union.d.ts vendored Normal file
View File

@ -0,0 +1,51 @@
/**
Create a union of types that share a common discriminant property.
Use-case: A shorter way to declare tagged unions with multiple members.
@example
```
import type {TaggedUnion} from 'type-fest';
type Tagged<Fields extends Record<string, unknown> = TaggedUnion<'type', Fields>
// The TaggedUnion utility reduces the amount of boilerplate needed to create a tagged union with multiple members, making the code more concise.
type EventMessage = Tagged<{
OpenExternalUrl: {
url: string;
id: number;
language: string;
};
ToggleBackButtonVisibility: {
visible: boolean;
};
PurchaseButtonPressed: {
price: number;
time: Date;
};
NavigationStateChanged: {
navigation?: string;
};
}>;
// Here is the same type created without this utility.
type EventMessage =
| {
type: 'OpenExternalUrl';
url: string;
id: number;
language: string;
}
| {type: 'ToggleBackButtonVisibility'; visible: boolean}
| {type: 'PurchaseButtonPressed'; price: number; time: Date}
| {type: 'NavigationStateChanged'; navigation?: string};
```
@category Utilities
*/
export type TaggedUnion<
TagKey extends string,
UnionMembers extends Record<string, Record<string, unknown>>,
> = {
[Name in keyof UnionMembers]: {[Key in TagKey]: Name} & UnionMembers[Name];
}[keyof UnionMembers];

30
test-d/tagged-union.ts Normal file
View File

@ -0,0 +1,30 @@
import {expectAssignable, expectNotAssignable} from 'tsd';
import type {TaggedUnion} from '../index';
type Union = TaggedUnion<'tag', {str: {a: string} ; num: {b: number}}>;
const first = {
tag: 'str' as const,
a: 'some-string',
};
const second = {
tag: 'num' as const,
b: 1,
};
expectAssignable<Union>(first);
expectAssignable<Union>(second);
const fails = {
tag: 'num' as const,
b: 'should not be string',
};
const failsToo = {
tag: 'str' as const,
b: 2,
};
expectNotAssignable<Union>(fails);
expectNotAssignable<Union>(failsToo);