2
votes

I want to create a function f that takes a string, and creates an object, where key is the only field set.

I also want the function to typecheck the interface A to make sure that the key-field is the only required field on the object. (There will be other optional fields).

Problem:

Is it possible to express the type A so that function f is valid – and doesn't produce a type error – and still typechecks A correctly when used?

export function f<A extends { key: string }>(key: string): A {
  return { key }; // This produces compile error TS2322: (see below)
}

// This be a few different interfaces, but they all have in common that
// the key-field is the only required field.
interface WithKey {
  key: string;
  ignoreMe?: string;
}

const result = f<WithKey>('myKey');

Compiler error:

TS2322: Type '{ key: string; }' is not assignable to type 'A'.   '{ key: string; }' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype of constraint '{ key: string; }'.

1

1 Answers

3
votes

The problem is that your statement says that it accepts a type that has to have key of string and this type will be returned.

it means if I pass there

{key: 1, requiredField: 5}

it returns the same type where I have requiredField.

But the implementation return { key } breaks this statement, because it doesn't return requiredField anymore. This causes TS2322.

A possible solution - simply say interface you return.

export function f(key: string): { key: string } { // <- return type
  return { key };
}

interface WithKey {
  key: string;
  ignoreMe?: string;
}

const result: WithKey = f('myKey');

result.key; // works
result.ignoreMe // works (undefined)