Skip to content

Commit

Permalink
SharedUnionFields: Simplify implementation (#1000)
Browse files Browse the repository at this point in the history
  • Loading branch information
som-sm authored Dec 10, 2024
1 parent 4ecd8ad commit 20e71e9
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 19 deletions.
30 changes: 11 additions & 19 deletions source/shared-union-fields.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {NonRecursiveType, IsUnion} from './internal';
import type {IsNever} from './is-never';
import type {Simplify} from './simplify';
import type {UnknownArray} from './unknown-array';

/**
Expand Down Expand Up @@ -62,22 +63,13 @@ function displayPetInfo(petInfo: SharedUnionFields<Cat | Dog>) {
@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;
type SharedUnionFields<Union> =
Extract<Union, NonRecursiveType | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | UnknownArray> extends infer SkippedMembers
? Exclude<Union, SkippedMembers> extends infer RelevantMembers
?
| SkippedMembers
| (IsNever<RelevantMembers> extends true
? never
: Simplify<Pick<RelevantMembers, keyof RelevantMembers>>)
: never
: never;
15 changes: 15 additions & 0 deletions test-d/shared-union-fields.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {expectType} from 'tsd';
import type {SharedUnionFields} from '../index';
import type {NonRecursiveType} from '../source/internal';

type TestingType = {
function: (() => void);
Expand Down Expand Up @@ -82,3 +83,17 @@ expectType<{union: 'test1' | 'test2' | {a: number}}>(union);

declare const unionWithOptional: SharedUnionFields<{a?: string; foo: number} | {a: string; bar: string}>;
expectType<{a?: string}>(unionWithOptional);

// Non-recursive types
expectType<Set<string> | Map<string, string>>({} as Set<string> | Map<string, string>);
expectType<string[] | Set<string>>({} as string[] | Set<string>);
expectType<NonRecursiveType>({} as NonRecursiveType);

// Mix of non-recursive and recursive types
expectType<{a: string | number} | undefined>({} as SharedUnionFields<{a: string} | {a: number; b: true} | undefined>);
expectType<RegExp | {test: string}>({} as SharedUnionFields<RegExp | {test: string}>);
expectType<RegExp | null | {test: string | number}>({} as SharedUnionFields<RegExp | null | {test: string} | {test: number; foo: any}>);

// Boundary types
expectType<any>({} as SharedUnionFields<any>);
expectType<never>({} as SharedUnionFields<never>);

0 comments on commit 20e71e9

Please sign in to comment.