Home > Software design >  Typescript - Creating a Type That Narrows Another Type
Typescript - Creating a Type That Narrows Another Type

Time:04-13

I have a scenario where I want to have multiple types/interfaces that are all in the same structure:

type Base = {
  type: 'x' | 'y' | 'z'
  data: Record<string,string>
}

Now, I want to create types that "implements" this base type, while each of them narrows this type in different way:

type MyTypeX = {
  type: 'x'
  data: {
    specificKey: string
  }
}

type MyTypeY = {
  type: 'y'
  data: {
    otherKey: string
  }
}

But I can't find any good way to do that. I know I can just use the types without having a base type, but I want to make sure that any of them has a "type" and "data" property and that each of them has a legal type. its like having a typesafe validation of other types. Is there any good way to do that? Is that even possible in TS?

CodePudding user response:

Maybe you can use a generic type Base. Something like the following:

type Type = 'x' | 'y' | 'z';

type Base<T extends Type, D extends Record<string, string>> = {
  type: T;
  data: D;
};

type Foo = Base<'x', { specificKey: string }>;
type Bar = Base<'y', { otherKey: string }>;

const foo: Foo = { type: 'x', data: { specificKey: 'foo' }};
const bar: Bar = { type: 'y', data: { otherKey: 'bar' }};

Playground Link

CodePudding user response:

If you can use interfaces, you can use the extends keyword, as in:

interface MyTypeX extends Base {
  type: 'x'
  data: {
    specificKey: string
  }
}

If you made something that was incompatible with Base (eg, you tried to do type: boolean), typescript will give an error where you define MyTypeX that MyTypeX incorrectly extends Base.

If you need to stick with types instead of interfaces, you could do an intersection of your type and the base type. If MyTypeX is incompatible with the base type, then MyTypeX will be never. This won't be an error where you define the type, but if you try to use the type you'll get errors.

type MyTypeX = Base & {
  type: 'x'
  data: {
    specificKey: string
  }
}

Playground link

  • Related