打字稿:从对象数组派生联合类型

时间:2020-03-02 20:37:12

标签: javascript typescript constants typeof keyof

我想声明一个类型强制的项数组,并能够从中派生联合类型。如果您未为数组中的项目显式指定类型,则此模式有效。我不确定如何最好地解释它,所以这里有一个例子:

示例1

type Pair = {
  key: string;
  value: number;
};

const pairs: ReadonlyArray<Pair> = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']

示例2

type Data = {
  name: string;
  age: number;
};

const DataRecord: Record<string, Data> = {
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
} as const;

type Keys = keyof typeof DataRecord;

以下是使用as const时派生密钥的示例。我想要这种行为,但是要显式键入数组。

const pairs = [
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
] as const;

type Keys = typeof pairs[number]['key']; // "foo" | "bar"

键的期望值:"foo"|"bar"

键的实际值:string

2 个答案:

答案 0 :(得分:5)

对于变量,您可以让编译器从初始化中推断类型,也可以明确地将其写出。如果像您一样显式地编写它,则将根据注释检查初始化值,但是初始化器的实际类型不会影响变量的类型(因此会丢失所需的类型信息)。如果让编译器进行推断,就不再可能限制类型以符合特定接口(如您所愿)

解决方案是使用泛型函数来约束值并推断其实际类型:

type Pair = {
  key: string;
  value: number;
};
function craetePairsArray<T extends readonly Pair[] & Array<{key: V}>, V extends string>(...args: T) {
    return args
}

const pairs = craetePairsArray(
  { key: 'foo', value: 1 },
  { key: 'bar', value: 2 },
)

type Keys1 = typeof pairs[number]['key']

type Data = {
  name: string;
  age: number;
};

function craeteDataObject<T extends Record<string, Data>>(arg: T) {
    return arg;
}
const DataRecord = craeteDataObject({
  foo: { name: 'Mark', age: 35 },
  bar: { name: 'Jeff', age: 56 },
})

type Keys2 = keyof typeof DataRecord;

Playground Link

注意:对于数组的情况,我们需要让编译器有力地为key推断字符串文字类型,从而推断整个& Array<{key: V}>,其中V是类型参数的扩展string

答案 1 :(得分:3)

通常的方法是:

  • 让TS通过省略显式类型pairs来推断ReadonlyArray<Pair>的类型(请参阅answer
  • key中的Pair类型"foo"|"bar"

如果您不想想要这样做,那么推断您的键限制pairs类型的唯一方法是使用助手功能。 Pair类型也将成为通用类型,以保存给定的key字符串文字类型。您可以使用IIFE使任务紧凑:

type Pair<K = string> = {
    key: K;
    value: number;
};

const pairs = (<T>(p: readonly Pair<T>[]) => p)([
    { key: 'foo', value: 1 },
    { key: 'bar', value: 2 },
] as const) // readonly Pair<"foo" | "bar">[]

type Keys = typeof pairs[number]['key'] // "foo" | "bar"

Playground