-
-
Notifications
You must be signed in to change notification settings - Fork 550
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
179 additions
and
8 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
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,83 @@ | ||
import type {NonRecursiveType, IsUnion} from './internal'; | ||
import type {IsNever} from './is-never'; | ||
import type {UnknownArray} from './unknown-array'; | ||
|
||
/** | ||
Create a type with shared fields from a union of object types. | ||
Use-cases: | ||
- You want a safe object type where each key exists in the union object. | ||
- You want to focus on the common fields of the union type and don't want to have to care about the other fields. | ||
@example | ||
``` | ||
import type {SharedUnionFields} from 'type-fest'; | ||
type Cat = { | ||
name: string; | ||
type: 'cat'; | ||
catType: string; | ||
}; | ||
type Dog = { | ||
name: string; | ||
type: 'dog'; | ||
dogType: string; | ||
}; | ||
function displayPetInfo(petInfo: Cat | Dog) { | ||
// typeof petInfo => | ||
// { | ||
// name: string; | ||
// type: 'cat'; | ||
// catType: string; // Needn't care about this field, because it's not a common pet info field. | ||
// } | { | ||
// name: string; | ||
// type: 'dog'; | ||
// dogType: string; // Needn't care about this field, because it's not a common pet info field. | ||
// } | ||
// petInfo type is complex and have some needless fields | ||
console.log('name: ', petInfo.name); | ||
console.log('type: ', petInfo.type); | ||
} | ||
function displayPetInfo(petInfo: SharedUnionFields<Cat | Dog>) { | ||
// typeof petInfo => | ||
// { | ||
// name: string; | ||
// type: 'cat' | 'dog'; | ||
// } | ||
// petInfo type is simple and clear | ||
console.log('name: ', petInfo.name); | ||
console.log('type: ', petInfo.type); | ||
} | ||
``` | ||
@see SharedUnionFieldsDeep | ||
@category Object | ||
@category Union | ||
*/ | ||
export type SharedUnionFields<Union> = | ||
// If `Union` is not a union type, return `Union` directly. | ||
IsUnion<Union> extends false | ||
? Union | ||
// `Union extends` will convert `Union` | ||
// to a [distributive conditionaltype](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types). | ||
// But this is not what we want, so we need to wrap `Union` with `[]` to prevent it. | ||
: [Union] extends [NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | UnknownArray] | ||
? Union | ||
: [Union] extends [object] | ||
// `keyof Union` can extract the same key in union type, if there is no same key, return never. | ||
? keyof Union extends infer Keys | ||
? IsNever<Keys> extends false | ||
? { | ||
[Key in keyof Union]: Union[Key] | ||
} | ||
: {} | ||
: Union | ||
: Union; |
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,84 @@ | ||
import {expectType} from 'tsd'; | ||
import type {SharedUnionFields} from '../index'; | ||
|
||
type TestingType = { | ||
function: (() => void); | ||
record: Record<string, { | ||
propertyA: string; | ||
}>; | ||
object: { | ||
subObject: { | ||
subSubObject: { | ||
propertyA: string; | ||
}; | ||
}; | ||
}; | ||
string: string; | ||
union: 'test1' | 'test2'; | ||
number: number; | ||
boolean: boolean; | ||
date: Date; | ||
regexp: RegExp; | ||
symbol: symbol; | ||
null: null; | ||
undefined: undefined; | ||
optional?: boolean | undefined; | ||
readonly propertyWithKeyword: boolean; | ||
map: Map<string, {propertyA: string; propertyB: string}>; | ||
set: Set<string> ; | ||
objectSet: Set<{propertyA: string; propertyB: string}>; | ||
}; | ||
|
||
declare const normal: SharedUnionFields<TestingType | {string: string; number: number; foo: any}>; | ||
expectType<{string: string; number: number}>(normal); | ||
|
||
declare const normal2: SharedUnionFields<TestingType | {string: string; foo: any}>; | ||
expectType<{string: string}>(normal2); | ||
|
||
declare const unMatched: SharedUnionFields<TestingType | {foo: any}>; | ||
expectType<{}>(unMatched); | ||
|
||
declare const number: SharedUnionFields<TestingType | {number: number; foo: any}>; | ||
expectType<{number: number}>(number); | ||
|
||
declare const string: SharedUnionFields<TestingType | {string: string; foo: any}>; | ||
expectType<{string: string}>(string); | ||
|
||
declare const boolean: SharedUnionFields<TestingType | {boolean: boolean; foo: any}>; | ||
expectType<{boolean: boolean}>(boolean); | ||
|
||
declare const date: SharedUnionFields<TestingType | {date: Date; foo: any}>; | ||
expectType<{date: Date}>(date); | ||
|
||
declare const regexp: SharedUnionFields<TestingType | {regexp: RegExp; foo: any}>; | ||
expectType<{regexp: RegExp}>(regexp); | ||
|
||
declare const symbol: SharedUnionFields<TestingType | {symbol: symbol; foo: any}>; | ||
expectType<{symbol: symbol}>(symbol); | ||
|
||
declare const null_: SharedUnionFields<TestingType | {null: null; foo: any}>; | ||
expectType<{null: null}>(null_); | ||
|
||
declare const undefined_: SharedUnionFields<TestingType | {undefined: undefined; foo: any}>; | ||
expectType<{undefined: undefined}>(undefined_); | ||
|
||
declare const optional: SharedUnionFields<TestingType | {optional: string; foo: any}>; | ||
expectType<{optional?: boolean | string | undefined}>(optional); | ||
|
||
declare const propertyWithKeyword: SharedUnionFields<TestingType | {readonly propertyWithKeyword: string; foo: any}>; | ||
expectType<{readonly propertyWithKeyword: boolean | string}>(propertyWithKeyword); | ||
|
||
declare const map: SharedUnionFields<TestingType | {map: Map<string, {propertyA: string}>; foo: any}>; | ||
expectType<{map: TestingType['map'] | Map<string, {propertyA: string}>}>(map); | ||
|
||
declare const set: SharedUnionFields<TestingType | {set: Set<number>; foo: any}>; | ||
expectType<{set: TestingType['set'] | Set<number>}>(set); | ||
|
||
declare const moreUnion: SharedUnionFields<TestingType | {string: string; number: number; foo: any} | {string: string; bar: any}>; | ||
expectType<{string: string}>(moreUnion); | ||
|
||
declare const union: SharedUnionFields<TestingType | {union: {a: number}}>; | ||
expectType<{union: 'test1' | 'test2' | {a: number}}>(union); | ||
|
||
declare const unionWithOptional: SharedUnionFields<{a?: string; foo: number} | {a: string; bar: string}>; | ||
expectType<{a?: string}>(unionWithOptional); |