-
-
Notifications
You must be signed in to change notification settings - Fork 508
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
Add RequireOnlyOne
type
#24
Changes from all commits
113d413
71046b6
922f79e
515178a
b1f5cd6
3ea0ec1
bb298a0
ff340bf
9312010
c2fc80d
5cba9c4
2fd59c9
15dc6dd
0600ec8
bacce8e
dfb66a4
df2a791
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,3 +198,36 @@ export type RequireAtLeastOne<ObjectType, KeysType extends keyof ObjectType = ke | |
}[KeysType] | ||
// …then, make intersection types by adding the remaining properties to each mapped type. | ||
& Omit<ObjectType, KeysType>; | ||
|
||
/* | ||
Create a type that requires at only one of the given properties. The remaining properties are kept as is. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you mention in text some real-world use-cases?
|
||
@example | ||
``` | ||
import {RequireOnlyOne} from 'type-fest'; | ||
|
||
type Responder = { | ||
text: () => string; | ||
json: () => string; | ||
|
||
secure: boolean; | ||
}; | ||
|
||
const responder: RequireOnlyOne<Responder, 'text' | 'json'> = { | ||
json: () => '{"message": "ok"}', | ||
secure: true | ||
}; | ||
``` | ||
*/ | ||
export type RequireOnlyOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Require only one" makes me think that it requires only one, but doesn't make it clear that it disallows more than one. Maybe we should simply name it |
||
{ | ||
// For each Key in KeysType make a mapped type | ||
[Key in KeysType]: ( | ||
// …by picking that Key's type and making it required | ||
Required<Pick<ObjectType, Key>> & | ||
// …and adding the other keys in KeysType as optional and of type `never` | ||
Partial<Record<Exclude<KeysType, Key>, never>> | ||
) | ||
}[KeysType] | ||
// …then, make intersection types by adding the remaining properties to each mapped type. | ||
& Omit<ObjectType, KeysType>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import {expectType, expectError} from 'tsd'; | ||
import {RequireOnlyOne} from '..'; | ||
|
||
type SystemMessages = { | ||
default: string; | ||
|
||
macos?: string; | ||
linux: string; | ||
windows: string; | ||
|
||
optional?: string; | ||
}; | ||
|
||
type ValidMessages = RequireOnlyOne<SystemMessages, 'macos' | 'linux' | 'windows'>; | ||
const test = (_: ValidMessages): void => {}; | ||
|
||
test({macos: 'hey', default: 'hello'}); | ||
test({linux: 'sup', default: 'hello'}); | ||
test({windows: 'hi', default: 'hello'}); | ||
test({windows: 'hi', default: 'hello', optional: 'howdy'}); | ||
|
||
const system = Math.random() > 0.5 ? {macos: 'hey'} : {linux: 'sup'}; | ||
test({default: 'hello', ...system}); | ||
|
||
expectError(test({})); | ||
expectError(test({macos: 'hey'})); | ||
expectError(test({default: 'hello'})); | ||
expectError(test({macos: 'hey', linux: 'sup', default: 'hello'})); | ||
expectError(test({macos: 'hey', default: 'hello', unknown: 'hmmm'})); | ||
|
||
// TODO: Should it eventually be possible to make this negative test pass? | ||
/* | ||
const invalidSystem = Math.random() > 0.5 ? {default: 'hello', macos: 'hey', a: 1, b: 3} : {default: 'hello', linux: 'sup', b: 2}; | ||
expectError(test(invalidSystem)); | ||
*/ | ||
|
||
declare const onlyOneWithoutKeys: RequireOnlyOne<{a: number; b: number}>; | ||
expectType<{a: number} | {b: number}>(onlyOneWithoutKeys); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?