Get typescript default/initial argument type in order to extend it

Viewed 230

I'm working on some code that auto-generates type hints based on function calls (GraphQL Nexus stuff).

Then, in one of it's functions, it expects an anonymous type based on those auto-generated attributes:

export const Item = {
  isTypeOf(data) {  // data's type is an anonymous type, here it would be '{ name: string }'
    ...
  }
  definition(t) {
    t.string('name')
  }
}

However, this data argument may contain more variables than the ones defined by the function calls. In my case, I need to access a kind property, but I can't call the t._type_ function because it would have undesired side-effects.

I also can't just pass the type as { kind: string } since isTypeOf type expects it to have at least all defined properties on it's arguments.

I could just use { name: string, kind: string } in this example, but my actual code contains more complex objects, and I would lose all the benefits of the auto-generated typing stuff.

Is there any way for me to in-line extend an argument with anonymous type? I was thinking something like an initial or default keyword to get an argument's own type, and use it like this:

isTypeOf(data: initial & { kind: string })
isTypeOf(data: default & { kind: string })
1 Answers

Typescript does not care about additional keys in objects passed to function (unless created in the parameter list).

If you only want to accept objects with given property and not return it, use the definition of isTypeOf.

If you need to return the same type and possibly extend it use the definition of definition.

export const Item = {
  isTypeOf(data: { kind: string }): boolean {
    return data.kind == '...';
  },
  definition<T extends { string: (key: string): void }>(t): T & { defined: true } {
    t.string('name');
    (t as T & { defined: true }).defined = true;

    return t;
  },
};

const myType = {
  kind: 'hello',
  name: 'World',
  string(key: string): {},
};

Item.isTypeOf(myType); // OK
Item.isTypeOf({ kind: 'hello', name: 'World' }); // Not OK

Item.definition(myType); // OK
Item.definition({ string(key: string): {} }); // OK
Related