Istvan Orban
Istvan Orban

Reputation: 1685

Pick from an intersection type? (TS)

I am quite new to TS and wrote a pick function but finding it difficult to pick from an intersection type:

type PaletteType = {
   black: string,
   white: string
}

type ColorType = {
   primaryColor: string,
   labelText: string,
}

type Props = {
   ...,
   backgroundColor: keyof ColorType | keyof PaletteType // (or would keyof (ColorType & PaletteType) would be better?
}

// Some general pick funtion
function pick<T extends { [key: string]: any }, K extends keyof T>(object: T, key?: K) {
    if (key) { return object[key] }
    return undefined
}

pick(Colors, props.backgroundColor) // error -> black | white not assignable to Colors

I am quite sure my 'solution' is kinda wrong:

backgroundColor: pick(Palette as typeof Palette & typeof Color, props.bg) || pick(Color as typeof Palette & typeof Color, props.bg),

Upvotes: 1

Views: 869

Answers (1)

jcalz
jcalz

Reputation: 328302

Adding these declarations to make the code compile:

declare const Colors: ColorType;
declare const Palette: PaletteType;
declare const props: Props;

In order to make your call to pick() type safe you can either merge Colors and Palette via something like object spread:

pick({ ...Colors, ...Palette }, props.backgroundColor); // okay

That works because {...Colors, ...Palette} is seen to be of type ColorType & PaletteType, whose keys are keyof ColorType | keyof PaletteType.

Or, you can make a user-defined type guard to narrow props.backgroundColor to either keyof ColorType or keyof PaletteType before calling pick():

const hasKey = <T extends object>(obj: T, key: keyof any): key is keyof T => key in obj;

hasKey(Colors, props.backgroundColor) ? 
  pick(Colors, props.backgroundColor) : 
  pick(Palette, props.backgroundColor); // okay

The former is probably neater.

By the way, I'm not sure what pick(o,k) buys you over just o[k], but that's up to you I guess.

Hope that helps; good luck!

Upvotes: 2

Related Questions