Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have mixed specific typed keys in a TypeScript type, and a generic key type?

I'm trying to create a type to describe an ES6 Proxy object where I will know the types for a few keys, and the rest of the keys will be generic with a callback as a value and I won't know their names until runtime.

However, if I try something like this:

interface MyCallback {
  (): void;
}

interface MyType {
    myKey1: number;
    [key: string]: MyCallBack;
}

I get errors like:

[ts] Property 'myKey1' of type 'number' is not assignable to string index type 'MyCallback'.

If I add [key: string]: number, I get the error Duplicate string index signature.

If I overload it so it's like number | MyCallback, I get this error if I try to call a callback on a MyType instance:

[ts] Cannot invoke an expression whose type lacks a call signature. Type 'number | MyCallback' has no compatible call signatures.

Is it possible to have a type like I'm trying to create in TypeScript?

like image 886
empyrical Avatar asked May 21 '18 18:05

empyrical


2 Answers

the answer is sort of. You can accomplish this with intersection types:

interface MyType {
    myKey1: number;
}

interface MyCallBack {
    (): void;
}

interface GenericCallbackType {
    [key: string]: MyCallBack;
}

type CombinedType = MyType & GenericCallbackType;

const obj: CombinedType = {
    myKey1: 8,
    anyString: () => {}
}
like image 166
bryan60 Avatar answered Sep 23 '22 12:09

bryan60


As noted in comments the accepted answer doesn't work with assignments, resulting in a Property 'myKey1' is incompatible with index signature error. To work with assignments we can utilize @jcalz's answer here:

interface MyCallback {
  (): void
}

interface MyType {
  myKey1: number
}

const asCombinedType = <C>(
  res: C & MyType & Record<Exclude<keyof C, keyof MyType>, MyCallback>
): C => res

const obj = asCombinedType({
  anyKey: () => { /* ...*/ },
  myKey1: 12
})

Admittedly a bit convoluted but it gets the job done.

like image 34
Aleksi Avatar answered Sep 24 '22 12:09

Aleksi