Skip to content

Commit

Permalink
ConditionalPickDeep: Supports interface (#776)
Browse files Browse the repository at this point in the history
  • Loading branch information
Emiyaaaaa committed Dec 4, 2023
1 parent 61a5325 commit ebb7a59
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 2 deletions.
3 changes: 2 additions & 1 deletion source/conditional-pick-deep.d.ts
Expand Up @@ -3,6 +3,7 @@ import type {ConditionalExcept} from './conditional-except';
import type {ConditionalSimplifyDeep} from './conditional-simplify';
import type {UnknownRecord} from './unknown-record';
import type {EmptyObject} from './empty-object';
import type {IsPlainObject} from './internal';

/**
Used to mark properties that should be excluded.
Expand Down Expand Up @@ -97,7 +98,7 @@ export type ConditionalPickDeep<
> = ConditionalSimplifyDeep<ConditionalExcept<{
[Key in keyof Type]: AssertCondition<Type[Key], Condition, Options> extends true
? Type[Key]
: Type[Key] extends UnknownRecord
: IsPlainObject<Type[Key]> extends true
? ConditionalPickDeep<Type[Key], Condition, Options>
: typeof conditionalPickDeepSymbol;
}, (typeof conditionalPickDeepSymbol | undefined) | EmptyObject>, never, UnknownRecord>;
11 changes: 11 additions & 0 deletions source/internal.d.ts
Expand Up @@ -3,6 +3,7 @@ import type {Simplify} from './simplify';
import type {Trim} from './trim';
import type {IsAny} from './is-any';
import type {UnknownRecord} from './unknown-record';
import type {UnknownArray} from './unknown-array';

// TODO: Remove for v5.
export type {UnknownRecord} from './unknown-record';
Expand Down Expand Up @@ -76,6 +77,16 @@ Matches non-recursive types.
*/
export type NonRecursiveType = BuiltIns | Function | (new (...args: any[]) => unknown);

/**
Returns a boolean for whether the given type is a plain key-value object.
*/
export type IsPlainObject<T> =
T extends NonRecursiveType | UnknownArray | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown>
? false
: T extends object
? true
: false;

export type UpperCaseCharacters = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z';

export type StringDigit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
Expand Down
27 changes: 26 additions & 1 deletion test-d/conditional-pick-deep.ts
Expand Up @@ -5,15 +5,22 @@ declare class ClassA {
public a: string;
}

// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface InterfaceA {
a: number;
}

type Example = {
optional?: boolean;
literal: 'foo';
string: string;
map: Map<string, string>;
set: Set<string>;
date: Date;
number: 1;
array: string[];
tuples: ['foo', 'bar'];
interface: InterfaceA;
instanceA: ClassA;
ClassA: typeof ClassA;
function: (...args: string[]) => string;
Expand All @@ -31,6 +38,9 @@ declare const stringPick: ConditionalPickDeep<Example, string>;
expectType<{
literal: 'foo';
string: string;
instanceA: {
a: string;
};
object: {
string: string;
subObject: {
Expand All @@ -42,6 +52,9 @@ expectType<{
declare const stringEqualityPick: ConditionalPickDeep<Example, string, {condition: 'equality'}>;
expectType<{
string: string;
instanceA: {
a: string;
};
object: {
string: string;
subObject: {
Expand All @@ -54,6 +67,9 @@ declare const stringPickOptional: ConditionalPickDeep<Example, string | undefine
expectType<{
literal: 'foo';
string: string;
instanceA: {
a: string;
};
object: {
string: string;
subObject: {
Expand All @@ -70,13 +86,19 @@ declare const booleanPick: ConditionalPickDeep<Example, boolean | undefined>;
expectType<{optional?: boolean | undefined}>(booleanPick);

declare const numberPick: ConditionalPickDeep<Example, number>;
expectType<{}>(numberPick);
expectType<{number: 1; interface: {a: number}}>(numberPick);

declare const emptyPick: ConditionalPickDeep<Example, 'abcdefg'>;
expectType<{}>(emptyPick);

declare const stringOrBooleanPick: ConditionalPickDeep<Example, string | boolean>;
expectType<{
literal: 'foo';
string: string;
stringOrBoolean: string | boolean;
instanceA: {
a: string;
};
object: {
string: string;
subObject: {
Expand Down Expand Up @@ -111,3 +133,6 @@ expectType<{map: Map<string, string>}>(mapPick);

declare const setPick: ConditionalPickDeep<Example, Set<string>>;
expectType<{set: Set<string>}>(setPick);

declare const interfaceTest: ConditionalPickDeep<Example, InterfaceA>;
expectType<{interface: InterfaceA}>(interfaceTest);

0 comments on commit ebb7a59

Please sign in to comment.