类型 'keyof T' 不满足约束

时间:2021-03-24 13:11:14

标签: typescript typescript-generics

尽管验证有效,但我仍收到此错误。我所需要的只是一个函数,它使用一个对象来生成另一个函数来过滤固定类型的参数

Type 'keyof T' does not satisfy the constraint '"a" | "b" | "c"'.
  Type 'string' is not assignable to type '"a" | "b" | "c"'.
    Type 'keyof T' is not assignable to type '"c"'.
      Type 'string' is not assignable to type '"c"'

type GM = {
    a: number;
    b: string;
    c: string
}

type VMeth<T, C > = (old: T, n: C) => any;

const getVF = <T extends { [key in keyof Partial<GM>]: 1 }>(part: T):  VMeth<GM, Pick<GM, keyof T>> => {
    return function(){ } as any
}

const fd = getVF({ a: 1 });

fd({ a: 1, b: "", c: "s"}, { a: 1 });

Playground link

1 个答案:

答案 0 :(得分:2)

约束

T extends { [key in keyof Partial<GM>]: 1 }

表示 T 必须可分配给 {a?:1, b?:1, c?:1}。这包括您尝试支持的类型,例如 {a: 1}。但它也包括您显然试图支持的类型。 TypeScript 中的对象类型是 extendableopen(相对于 exactclosed)。您可以通过向对象类型添加属性来扩展对象类型。因此也支持 {a?: 1, b?:1, c?:1, oops: string} 类型:

const oopsie = getVF({ a: 1, oops: "oops" }) // no error!
// const oopsie: VMeth<GM, Pick<GM, "a" | "oops">>

因为 T 实际上可能比 GM 拥有更多的键,编译器理所当然地抱怨

// Type 'keyof T' does not satisfy the constraint 'keyof GM'.

如果你真的想把 part 的键限制为 GM 的键(或者至少只关注那些键,因为无论你做什么对象类型都是开放的),你可以改为在这些键 K 中使您的函数通用:

const getVF = <K extends keyof GM>(part: Record<K, 1>):
    VMeth<GM, Pick<GM, K>> => {
    return function () { } as any
}

现在 K 必须是 "a" | "b" | "c" 并集的某个子集,不能是 "oops" 或其他任何东西。因此 Pick<GM, K> 将始终有效。您所需的用例仍然具有相同的功能:

fd({ a: 1, b: "", c: "s" }, { a: 1 });
// const fd: (old: GM, n: Pick<GM, "a">) => any

现在,如果我们明显添加了意外的属性,我们会收到编译器警告:

getVF({ a: 1, oops: "oops" }); // error, excess property!

如果你偷偷摸摸,你仍然可以设法在那里获得如此多的财产:

const existingObj = {a: 1, oops: "oops"} as const;
const aliased: {a: 1} = existingObj;
const okay = getVF(aliased); // no error
// const okay: VMeth<GM, Pick<GM, "a">>

但至少出来的值仍然是 Pick<GM, "a"> 而不是像 Pick<GM, "a" | "oops"> 这样的无效值。


Playground link to code