Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I get this error despite the validation working. All I need is a function that generates another function using an object to filter arguments of a fixed type
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
–
–
T extends { [key in keyof Partial<GM>]: 1 }
means that T must be assignable to {a?:1, b?:1, c?:1}. This includes types you are trying to support, like {a: 1}. But it also includes types you are apparently not trying to support. Object types in TypeScript are extendable or open (as opposed to exact or closed). You are allowed to extend an object type by adding properties to it. So the type {a?: 1, b?:1, c?:1, oops: string} is also supported:
const oopsie = getVF({ a: 1, oops: "oops" }) // no error!
// const oopsie: VMeth<GM, Pick<GM, "a" | "oops">>
Because T may in fact have more keys than GM, the compiler rightfully complains that
// Type 'keyof T' does not satisfy the constraint 'keyof GM'.
If you really want to limit the keys of part to those of GM (or at least only pay attention to those keys, since object types are open no matter what you do), you can make your function generic in those keys K instead:
const getVF = <K extends keyof GM>(part: Record<K, 1>):
VMeth<GM, Pick<GM, K>> => {
return function () { } as any
Now K must be some subset of the union of "a" | "b" | "c" and cannot be "oops" or anything else. And so Pick<GM, K> will always work. Your desired use case still functions the same:
fd({ a: 1, b: "", c: "s" }, { a: 1 });
// const fd: (old: GM, n: Pick<GM, "a">) => any
And now we get compiler warnings if we obviously add an unexpected property:
getVF({ a: 1, oops: "oops" }); // error, excess property!
You can still manage to get such an excess property in there if you are sneaky:
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">>
But at least the value that comes out is still Pick<GM, "a"> and not something invalid like Pick<GM, "a" | "oops">.
Playground link to code
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.