【问题标题】:Typescript: derive union type from array of objects打字稿:从对象数组派生联合类型
【发布时间】:2020-06-15 04:30:15
【问题描述】:

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

示例 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

【问题讨论】:

  • 我不认为您可以按照您尝试的方式动态地执行此操作,您将运行时值与编译时类型混为一谈。您必须为 Pair 类型的 key 属性提供您想要的类型,然后它应该像您编写的那样工作。
  • @JaredSmith 这在运行时应该不是问题。我用它来声明任意数量的在执行期间不会改变的值。这相当于在类型声明中设置 key: "foo"|"bar"。
  • "这在运行时应该不是问题" --- typescript 没有运行时,所以在运行时是一个问题。
  • @Ben 请允许我说得更具体一点:我认为您不能像使用不可变原语元组那样使用从可变引用类型中提取的属性元组来做到这一点。你可以说possibleKeys = ['foo', 'bar'] as const; type Keys = typeof possibleKeys[number]; type Pair = { key: Keys, value: number };,但你仍然需要明确列举可能的键。

标签: javascript typescript constants typeof keyof


【解决方案1】:

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

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

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

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

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

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

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

type Keys2 = keyof typeof DataRecord;

Playground Link

注意:对于数组的情况,我们需要稍微加强编译器以推断key 的字符串文字类型,因此整个&amp; Array&lt;{key: V}&gt;,其中V 是扩展string 的类型参数

【讨论】:

  • 谢谢!这正是我所需要的!
【解决方案2】:

通常的做法是:

  • 让 TS 通过省略显式类型 ReadonlyArray&lt;Pair&gt; 来推断 pairs 的类型(参见 answer)
  • 给key in 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

【讨论】:

    猜你喜欢
    • 2017-12-28
    • 1970-01-01
    • 2021-09-16
    • 1970-01-01
    • 2020-10-01
    • 2021-06-27
    • 2018-09-15
    相关资源
    最近更新 更多