0

I've defined a function type in TypeScript. But I can't instantiate it. Why does TypeScript give a compile error? This structure works fine without type arguments, and for generic signatures where I do not use key of as a constraint. When I remove the type argument from replaceProperty it does what I want - the code seems to be correct.

type ReplaceProperty<T, K extends keyof T, V> = (
  i: T,
  k: K,
  v: V
) => { [P in keyof T]: K extends P ? V : T[P] };

// Compile error
// Generic type 'ReplaceProperty' requires 3 type argument(s).
const replaceProperty: ReplaceProperty = <T, K extends keyof T, V>(
  i: T,
  k: K,
  v: V
) => ({
  ...i,
  [k]: v,
});

1 Answer 1

3

type ReplaceProperty<T, K extends keyof T, V> describes a function where the generic type parameters T, K, and V are specified in advance. The function can only be called with args of these predetermined types (i: T, k: K, v: V).

It is a generic type which describes a function that is not generic.

What you want is a generic function. A generic function can be called with arguments of differing types. The type parameters T, K, and V are different for each function call and are determined based on the arguments when the function is called.

You need to move the generics to the other side of the equals sign:

type ReplaceProperty = <T, K extends keyof T, V>(

This describes the type accurately, but you get errors on your implementation replaceProperty because Typescript sees the return type as a mismatch.

Type '<T, K extends keyof T, V>(i: T, k: K, v: V) => T & { [x: string]: V; }' is not assignable to type 'ReplaceProperty'.

Type 'T & { [x: string]: V; }' is not assignable to type '{ [P in keyof T]: K extends P ? V : T[P]; }'

Setting the dynamic property {[k]: v} creates a string index signature {[x: string]: V;}


You probably need to assert correctness with as. I am using parenthesis because I am asserting the type for the entire function, not for the return value.

const replaceProperty = (<T, K extends keyof T, V>(
  i: T,
  k: K,
  v: V
) => ({
    ...i,
  [k]: v,
})) as ReplaceProperty;

But I'm not sure that there's really a point to creating a type for the function rather than for it's arguments and returned value. Perhaps this inline declaration makes more sense.

const replaceProperty1 = <T, K extends keyof T, V>(
  i: T,
  k: K,
  v: V
) => ({
    ...i,
  [k]: v,
}) as { [P in keyof T]: K extends P ? V : T[P] }

Typescript Playground Link

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the thoughtful answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.