TypeScript: Union of a specific property of items in array

后端 未结 1 374
小蘑菇
小蘑菇 2021-01-27 02:49

TLDR; VERSION

Edit, I have added a Playground, Link at the bottom

I have an array ob objects in TypeScrip

相关标签:
1条回答
  • 2021-01-27 03:32

    The first problem I see is that your type annotations of infoConverter as Converter<string> and pressureConverter as Converter<number> are too wide. The compiler will dutifully forget that infoConverter.name is "info", and instead will use the type annotation that says it is a string.

    I think you will get closer to what you want by just using type inference and a const assertion to get the narrowest possible types for your converter instances:

    const infoConverter = {
      name: "info",
      uuid: "180a",
      decode: (v: Buffer) => v.toString()
    } as const;
    
    const pressureConverter = {
      name: "pressure",
      uuid: "1810",
      decode: (v: Buffer) => v / 1024
    } as const;
    
    const converters = [infoConverter, pressureConverter] as const;
    

    At this point there's no guarantee that the elements of converters conform to your Converter interface, but you can make sure that any functions that accept converters or types that accept typeof converters are constrained to Converter<any>[] and you'll get an error there if converters has a mistake.

    So let's look at those two type functions you want... given an array of Converter types, get the list of names, and a mapping of name to value type. How about:

    type Names<C extends ReadonlyArray<Converter<any>>> = C[number]["name"];
    
    type Value<
      C extends ReadonlyArray<Converter<any>>,
      N extends Names<C>
    > = Extract<C[number], { name: N }> extends Converter<infer V> ? V : never;
    

    Note that I constrain C to ReadonlyArray<Converter<any>> and not Array<Converter<any>>... this is a looser constraint (the Array interface is an extension of the ReadonlyArray interface; every Array is a ReadonlyArray but not vice versa; the naming is perhaps a bit misleading; maybe it should be ReadableAndWritableArray for Array and ReadableArray for ReadonlyArray... but I digress). That's so that C will accept typeof converters, which is a readonly tuple.

    Anyway, the Names type alias looks up the "name" property type from the number-indexed elements of the C array. And the Value type alias Extracts the elements of C that have a name property matching N, and then infers the value type from it.

    Does it work?

    type TheNames = Names<typeof converters>; // "info" | "pressure"
    type InfoValue = Value<typeof converters, "info">; // string
    type PressureValue = Value<typeof converters, "pressure">; // number
    

    Looks good to me. Note that the above lines have no compiler errors, so the converters object must be correctly meeting the constraint of being a (possibly readonly) array of Converter elements.

    Hopefully you can use this to get your expanded example code working. Good luck!

    Link to code

    0 讨论(0)
提交回复
热议问题