首页 > 解决方案 > TypeScript 类型是其他类型键的子集?

问题描述

给定一个对象,我想创建第二个类型化对象,它具有第一个键的子集和不同的值类型。我试过Partial<keyof ...>了,但这似乎没有效果。有没有其他方法可以输入第二个对象(mapping在下面的示例中)。

示例:(在 TypeScript Playground 上

const obj = {
    a: '1',
    b: '2',
    c: 3,
    d: 4,
    e: false,
    f: 'Hmmm',
};

// Below gives error:
// Type '{ a: number; b: number; }' is missing the following properties from type 'Record<"a" | "b" | "c" | "d" | "e" | "f", number>': c, d, e, f(2739)
const mapping: Record<keyof typeof obj, number> = {
    a: 10,
    b: 20
};


// basically same error using Partial<>
// Type '{ a: number; b: number; }' is missing the following properties from type 'Record<Partial<"a" | "b" | "c" | "d" | "e" | "f">, number>': c, d, e, f(2739)
const mapping2: Record<Partial<keyof typeof obj>, number> = {
    a: 10,
    b: 20
};

// basically same error using Partial<> a little differently
// Type '{ a: number; b: number; }' is missing the following properties from type 'Record<Partial<"a" | "b" | "c" | "d" | "e" | "f">, number>': c, d, e, f(2739)
const mapping3: Record<keyof Partial<typeof obj>, number> = {
    a: 10,
    b: 20
};

标签: typescript

解决方案


Partial<>仅将一个类型的所有属性标记为可选。我认为您正在寻找的是一种修改,Pick<>它会创建一个具有另一种类型的属性子集的新类型。

虽然保留了选择的属性类型,但您需要将它们映射到新类型。

您可以为此创建类型:

type PickNumber<Type, Keys extends keyof Type> = {
    [K in Keys]: number;
};

const value: PickNumber<typeof obj, 'a' | 'b'> = {
  a: 1,   // ok
  b: '2', // bad type
  c: 3,   // invalid property
};

话虽如此,你想要得到的是一个类型的键集的一个子集。您可以使用Extract<>.

Pick<>已经为您执行此操作,但这是等效的:

type PickAlt<Type, Keys> = {
  [K in Extract<keyof Type, Keys>]: Type[K];
};

所以Extract<>在这里申请但内联:

const value: {[K in Extract<keyof typeof obj, 'a' | 'b'>]: number} = ...;

或者就Record<>类型而言:

const value: Record<Extract<keyof typeof obj, 'a' | 'b'>, number> = ...;

操场


推荐阅读