Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
OptionalKeysOf
, HasOptionalKeys
, RequiredKeysOf
, `HasRequir…
…edKeys` types (#405) Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
- Loading branch information
1 parent
1483de3
commit f0b1c3f
Showing
10 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {OptionalKeysOf} from './optional-keys-of'; | ||
|
||
/** | ||
Creates a type that represents `true` or `false` depending on whether the given type has any optional fields. | ||
This is useful when you want to create an API whose behavior depends on the presence or absence of optional fields. | ||
@example | ||
``` | ||
import type {HasOptionalKeys, OptionalKeysOf} from 'type-fest'; | ||
type UpdateService<Entity extends object> = { | ||
removeField: HasOptionalKeys<Entity> extends true | ||
? (field: OptionalKeysOf<Entity>) => Promise<void> | ||
: never | ||
} | ||
``` | ||
@category Utilities | ||
*/ | ||
export type HasOptionalKeys<BaseType extends object> = OptionalKeysOf<BaseType> extends never ? false : true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import {RequiredKeysOf} from './required-keys-of'; | ||
|
||
/** | ||
Creates a type that represents `true` or `false` depending on whether the given type has any required fields. | ||
This is useful when you want to create an API whose behavior depends on the presence or absence of required fields. | ||
@example | ||
``` | ||
import type {HasRequiredKeys} from 'type-fest'; | ||
type GeneratorOptions<Template extends object> = { | ||
prop1: number; | ||
prop2: string; | ||
} & (HasRequiredKeys<Template> extends true | ||
? {template: Template} | ||
: {template?: Template}); | ||
interface Template1 { | ||
optionalSubParam?: string; | ||
} | ||
interface Template2 { | ||
requiredSubParam: string; | ||
} | ||
type Options1 = GeneratorOptions<Template1>; | ||
type Options2 = GeneratorOptions<Template2>; | ||
const optA: Options1 = { | ||
prop1: 0, | ||
prop2: 'hi' | ||
}; | ||
const optB: Options1 = { | ||
prop1: 0, | ||
prop2: 'hi', | ||
template: {} | ||
}; | ||
const optC: Options1 = { | ||
prop1: 0, | ||
prop2: 'hi', | ||
template: { | ||
optionalSubParam: 'optional value' | ||
} | ||
}; | ||
const optD: Options2 = { | ||
prop1: 0, | ||
prop2: 'hi', | ||
template: { | ||
requiredSubParam: 'required value' | ||
} | ||
}; | ||
``` | ||
@category Utilities | ||
*/ | ||
export type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
Extract all optional keys from the given type. | ||
This is useful when you want to create a new type that contains different type values for the optional keys only. | ||
@example | ||
``` | ||
import type {OptionalKeysOf, Except} from 'type-fest'; | ||
interface User { | ||
name: string; | ||
surname: string; | ||
luckyNumber?: number; | ||
} | ||
const REMOVE_FIELD = Symbol('remove field symbol'); | ||
type UpdateOperation<Entity extends object> = Except<Partial<Entity>, OptionalKeysOf<Entity>> & { | ||
[Key in OptionalKeysOf<Entity>]?: Entity[Key] | typeof REMOVE_FIELD; | ||
}; | ||
const update1: UpdateOperation<User> = { | ||
name: 'Alice' | ||
}; | ||
const update2: UpdateOperation<User> = { | ||
name: 'Bob', | ||
luckyNumber: REMOVE_FIELD | ||
}; | ||
``` | ||
@category Utilities | ||
*/ | ||
export type OptionalKeysOf<BaseType extends object> = Exclude<{ | ||
[Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> | ||
? never | ||
: Key | ||
}[keyof BaseType], undefined>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/** | ||
Extract all required keys from the given type. | ||
This is useful when you want to create a new type that contains different type values for the required keys only or use the list of keys for validation purposes, etc... | ||
@example | ||
``` | ||
import type {RequiredKeysOf} from 'type-fest'; | ||
declare function createValidation<Entity extends object, Key extends RequiredKeysOf<Entity> = RequiredKeysOf<Entity>>(field: Key, validator: (value: Entity[Key]) => boolean): ValidatorFn; | ||
interface User { | ||
name: string; | ||
surname: string; | ||
luckyNumber?: number; | ||
} | ||
const validator1 = createValidation<User>('name', value => value.length < 25); | ||
const validator2 = createValidation<User>('surname', value => value.length < 25); | ||
``` | ||
@category Utilities | ||
*/ | ||
export type RequiredKeysOf<BaseType extends object> = Exclude<{ | ||
[Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> | ||
? Key | ||
: never | ||
}[keyof BaseType], undefined>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {expectType} from 'tsd'; | ||
import type {HasOptionalKeys} from '../index'; | ||
|
||
type TestType1 = { | ||
a: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType2 = { | ||
a?: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType3 = { | ||
a: string; | ||
b: boolean; | ||
}; | ||
|
||
type HasOptionalKeys1 = HasOptionalKeys<TestType1>; | ||
type HasOptionalKeys2 = HasOptionalKeys<TestType2>; | ||
type HasOptionalKeys3 = HasOptionalKeys<TestType3>; | ||
|
||
declare const test1: HasOptionalKeys1; | ||
declare const test2: HasOptionalKeys2; | ||
declare const test3: HasOptionalKeys3; | ||
|
||
expectType<true>(test1); | ||
expectType<true>(test2); | ||
expectType<false>(test3); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {expectType} from 'tsd'; | ||
import type {HasRequiredKeys} from '../index'; | ||
|
||
type TestType1 = { | ||
a: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType2 = { | ||
a?: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType3 = { | ||
a: string; | ||
b: boolean; | ||
}; | ||
|
||
type HasRequiredKeys1 = HasRequiredKeys<TestType1>; | ||
type HasRequiredKeys2 = HasRequiredKeys<TestType2>; | ||
type HasRequiredKeys3 = HasRequiredKeys<TestType3>; | ||
|
||
declare const test1: HasRequiredKeys1; | ||
declare const test2: HasRequiredKeys2; | ||
declare const test3: HasRequiredKeys3; | ||
|
||
expectType<true>(test1); | ||
expectType<false>(test2); | ||
expectType<true>(test3); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {expectType} from 'tsd'; | ||
import type {OptionalKeysOf} from '../index'; | ||
|
||
type TestType1 = { | ||
a: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType2 = { | ||
a?: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType3 = { | ||
a: string; | ||
b: boolean; | ||
}; | ||
|
||
type OptionalKeysOf1 = OptionalKeysOf<TestType1>; | ||
type OptionalKeysOf2 = OptionalKeysOf<TestType2>; | ||
type OptionalKeysOf3 = OptionalKeysOf<TestType3>; | ||
|
||
declare const test1: OptionalKeysOf1; | ||
declare const test2: OptionalKeysOf2; | ||
declare const test3: OptionalKeysOf3; | ||
|
||
expectType<'b'>(test1); | ||
expectType<'a' | 'b'>(test2); | ||
expectType<never>(test3); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {expectType} from 'tsd'; | ||
import type {RequiredKeysOf} from '../index'; | ||
|
||
type TestType1 = { | ||
a: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType2 = { | ||
a?: string; | ||
b?: boolean; | ||
}; | ||
|
||
type TestType3 = { | ||
a: string; | ||
b: boolean; | ||
}; | ||
|
||
type RequiredKeysOf1 = RequiredKeysOf<TestType1>; | ||
type RequiredKeysOf2 = RequiredKeysOf<TestType2>; | ||
type RequiredKeysOf3 = RequiredKeysOf<TestType3>; | ||
|
||
declare const test1: RequiredKeysOf1; | ||
declare const test2: RequiredKeysOf2; | ||
declare const test3: RequiredKeysOf3; | ||
|
||
expectType<'a'>(test1); | ||
expectType<never>(test2); | ||
expectType<'a' | 'b'>(test3); |