代码之家  ›  专栏  ›  技术社区  ›  Jeremy Danyow

如何缩小基于Record键值的可区分并集

  •  0
  • Jeremy Danyow  · 技术社区  · 1 年前

    我有一个并集类型,它的元素有一个名为“type”的鉴别器属性。然后我声明一个记录<>其键是“类型”属性值。

    如何声明Record的第二个通用参数(Record<, 这个参数 >)使得基于对应的键来推断映射/记录中的值?

    TypeScript Playground

    interface Dog {
      type: 'dog';
      breed: string;
    }
    
    interface Vehicle {
      type: 'vehicle';
      max_speed: number;
    };
    
    type Thing = Dog | Vehicle;
    
    const printMap: Record<Thing['type'], (thing: Thing) => string> = {
      'dog': x => `Dog ${x.breed}`,
                        // ^^^^^ Property 'breed' does not exist on type 'Thing'. Property 'breed' does not exist on type 'Vehicle'.
                        // How do I make TypeScript infer `x` is a Dog because the key/property name is 'dog'?
      'vehicle': x => `Vehicle max spped ${x.max_speed}`
                                          // ^^^^^^^^^ Property 'max_speed' does not exist on type 'Thing'. Property 'max_speed' does not exist on type 'Dog'.
                                          // How do I make TypeScript infer `x` is a Vehicle because the key/property name is 'vehicle'?
    }
    
    1 回复  |  直到 1 年前
        1
  •  1
  •   Jeremy Danyow    1 年前

    你不能使用 Record<K, V> 为此,因为每个属性值需要不同的类型,但是 记录<K、 V> 具有相同的属性类型 V 对于每个键。相反,您需要构建 mapped type 那个 Extract s 受歧视的工会中每一财产的相关成员。可能是这样的:

    type DiscriminatedUnionHandler<T extends Record<K, PropertyKey>, K extends keyof T, R> =
      { [P in T[K]]: (foo: Extract<T, Record<K, P>>) => R }
    

    给你 DiscriminatedUnionHandler<T, K, R> 采用有区别的并集类型 T ,其判别键名称 K ,以及返回类型 R 的单个处理程序函数。生成的类型具有的每个值的属性 T[K] (即 "dog" | "vehicle" 对于 Thing )并且相应的属性是接受的相关成员的函数 t 并返回 r

    对于 事情 这看起来像:

    type DiscriminatedUnionHandler<T extends Record<K, PropertyKey>, K extends keyof T, R> =
      { [P in T[K]]: (foo: Extract<T, Record<K, P>>) => R }
    
    interface Dog {
      type: 'dog';
      breed: string;
    }
    interface Vehicle {
      type: 'vehicle';
      max_speed: number;
    };
    
    type Thing = Dog | Vehicle;
    
    const printMap: DiscriminatedUnionHandler<Thing, "type", string> = {
      'dog': x => `Dog ${x.breed}`,
      'vehicle': x => `Vehicle max spped ${x.max_speed}`
    }
    

    Playground link to code