Home > database >  Typescript: widening a JSON type to accept interfaces
Typescript: widening a JSON type to accept interfaces

Time:12-07

This is an extension of Typescript: passing interface as parameter for a function that expects a JSON type (asking about passing interfaces to JSON typed functions), which in turn is an extension of Typescript: interface that extends a JSON type (asking about casting to/from JSON types)

These questions relate to a JSON Typescript type:

type JSONValue = 
 | string
 | number
 | boolean
 | null
 | JSONValue[]
 | {[key: string]: JSONValue}

In Typescript: passing interface as parameter for a function that expects a JSON type, the final answer indicates that it is not possible to pass an interface to a function that expects a JSON value. In particular, the following code:

interface Foo {
  name: 'FOO',
  fooProp: string
}

const bar = (foo: Foo) => { return foo }

const wrap = <T extends JSONValue[]>(
  fn: (...args: T) => JSONValue, 
  ...args: T
) => {
  return fn(...args);
}

wrap(bar, { name: 'FOO', fooProp: 'hello'});

fails because the interface Foo cannot be assigned to JSONValue even though analytically it is easy to recognize that the cast should be fine.

see playground, as well as https://github.com/microsoft/TypeScript/issues/15300

The previous answer stated:

The only workaround we have without widening the JSONValue type is to convert [interface] Foo to be a type.

In my case, I can modify the JSONValue type but cannot easily modify all of the relevant interfaces. What would widening the JSONValue type entail?

CodePudding user response:

If you use a separate generic for the return type (which also extends JsonValue, then you won't have a type compatibility issue, and you can also infer a more narrow return type:

function wrap <A extends JsonValue[], T extends JsonValue>(
  fn: (...args: A) => T,
  ...args: A
): T {
  return fn(...args);
}

const result = wrap(bar, { name: 'FOO', fooProp: 'hello'});  // ok            
  • Related