Search code examples
typescripttypeguards

use of the "in" type guard in typescript


I am trying to create a type guard - to allow typesafe conversion of a series of shorthand css properties, to their longhand version - but do so without having to cast the rule back to keyof SxProps at the end.

type SxProps = { w?: string; width?: string; h?: string; height?: string; }
const shorthandCssRuleMapping = {
  w: 'width',
  h: 'height'
} as const;

export function hasKey<K extends string>(
  key: K,
  object: {}
): object is { [_ in K]: {} } {
  return typeof object === "object" && object !== null && key in object;
}

export function convertShorthandRule(rule: keyof SxProps) {
  let longHandRule = rule;
  if (hasKey(rule, shorthandCssRuleMapping)) {
    let pickedShortHandRule = shorthandCssRuleMapping[rule];
    // @NOTE: REALLY wish we didn't have to cast here...
    longHandRule = pickedShortHandRule as keyof SxProps;
  }
  return longHandRule;
}

basically, the type of pickedShorthandRule is this: {} | "width" | "height" before we cast it. Im pretty sure this is from a dodgy type predicate - but cant work out how to fix it. :P

I really need it to be: "width" | "height" - so that I can get rid of the cast: as keyof SxProps

Help!


Solution

  • You can change object is { [_ in K]: {} } to object is { [_ in K]: keyof SxProps } to make sure the values in shorthandCssRuleMapping will be always from long-hand keys

    type SxProps = { w?: string; width?: string; h?: string; height?: string; }
    const shorthandCssRuleMapping = {
      w: 'width',
      h: 'height'
    } as const;
    
    export function hasKey<K extends string>(
      key: K,
      object: {}
    ): object is { [_ in K]: keyof SxProps } {
      return typeof object === "object" && object !== null && key in object;
    }
    
    export function convertShorthandRule(rule: keyof SxProps) {
      let longHandRule = rule;
      if (hasKey(rule, shorthandCssRuleMapping)) {
        let pickedShortHandRule = shorthandCssRuleMapping[rule];
        longHandRule = pickedShortHandRule;
      }
      return longHandRule;
    }
    

    Playground