Jiajun
Jiajun

Reputation: 155

For conditional type in object key, how to remove certain key to produce a subset object type?

Here is how I played with some advanced types. I want to make a config type that extends BaseConfig. With this config type, inter to a dataProducer type (Named as Conf in the example) that has the same property that is marked as 'yes' in the config type.

interface BaseConfig1 {
    a: 'yes'|'no';
    b: 'yes'|'no';
}

interface BaseConfig2 {
    a?: 'yes';
    b?: 'yes';
}

interface Config1 extends BaseConfig1 {
    a: 'yes';
}

interface Config2 extends BaseConfig2 {
    a: 'yes';
}


type Conf<T, D> = {
    [P in keyof T]: T[P] extends 'yes'? ((data:D) => string): never;
};

// Error. Missing property b.
const dataProducer1: Conf<Config1, any> = {a: (data:any) => ''};

// Works
const dataProducer2: Conf<Config2, any> = {a: (data:any) => ''};

Ideally, BaseConfig1 is what I actually want to define. You need to mark 'Yes' in the child type to config the output type. But TS insist me to provide a b property whose type is never. Aren't never supposed to be left out in the types just like the Filter type? type Filter<T, U> = T extends U ? T : never;

The best I can do is BaseConfig2, any better ideas?

Update: I do want to force the result type to have properties marked as 'yes' type. For the above example, the result type should have "a" property.

Update2: Another question, for BaseConfig2, b's type is 'yes'|undefined, so why the Conf<Config2, any> would expect a optional b property with undefined type instead of a b with never type.

Update3: Tried a different way to define Conf, but doesn't work.

type Conf<T, D> = {
    [P in keyof T & T[P] extends 'yes']: (data:D) => string;
};

Upvotes: 0

Views: 933

Answers (1)

Ibraheem
Ibraheem

Reputation: 2358

interface BaseConfig {
  a: 'yes'|'no';
  b: 'yes'|'no';
}

interface BaseConfigD {
  a: (data:any) => string;
  b: (data:any) => string;
}

interface Config extends BaseConfig {
  a: 'yes';
}

// Extract the property names that extend 'yes'
type ConfProps<T> = {
  [P in keyof T]: T[P] extends 'yes'? P: never;
}[keyof T];

// Properties of Config that extend 'yes'
type ConfigProps = ConfProps<Config>;

// Properties of BaseConfigD that extend 'yes' from Config..
type BaseConfigDBasedOnConfigProps = Pick<BaseConfigD, ConfigProps>;

const dataProducer1: BaseConfigDBasedOnConfigProps = {a: (data:any) => ''};

Upvotes: 1

Related Questions