Home > database >  Dynamic type based function result
Dynamic type based function result

Time:11-21

I have an enum, representing type of node in tree structure.

enum MyEnum {
  'A' = 'A',
  'B' = 'B',
  'C' = 'C',
  // ...
}

Each node has predefined allowed types of children nodes.

type ChildrenAllowed<T extends MyEnum> = T extends MyEnum.A
  ? MyEnum.B | MyEnum.C
  : T extends MyEnum.B
  ? MyEnum.C
  : undefined;

I would like to have a function, that returns an array of allowed node types. However, I'm getting type errors. Is there something I'm missing?

const getChildrenAllowedTypes = <T extends MyEnum>(
  p: T,
): ChildrenAllowed<T>[] => {
  switch (p) {
    case MyEnum.A:
      return [
        MyEnum.B, // Err: Type 'E.B' is not assignable to type 'O'.
        MyEnum.C, // Err: Type 'E.C' is not assignable to type 'O'.
      ];
    case MyEnum.B:
      return [
        MyEnum.C, // Err: Type 'E.C' is not assignable to type 'O'.
      ];
    default:
      return [];
  }
};

This is not related to the problem directly.

// simplified object structure

type ChildrenOwner<TCurrent extends MyEnum> = {
  children?: Children<TCurrent>;
};

type Children<
  TCurrent extends MyEnum,
  TChildrenAllowed = ChildrenAllowed<TCurrent>,
> = {
  [TKey in TChildrenAllowed & string]?: TKey extends MyEnum
    ? ChildrenOwner<TKey>
    : undefined;
};

CodePudding user response:

It's always the same problem: TS won't compute the value of ChildrenAllowed<T> because T is a generic and is thus undetermined.

I don't know why you get the error not assignable to type 'O': it should be not assignable to 'typeChildrenAllowed<T>' but the bottom line is you need to assert your way out of this.

type ChildrenAllowed<T extends MyEnum> =
  T extends MyEnum.A
  ? MyEnum.B | MyEnum.C
  : T extends MyEnum.B
  ? MyEnum.C
  : never; // I think it should be never

const getChildrenAllowedTypes =
  <T extends MyEnum>(p: T): ChildrenAllowed<T>[] => {
    switch (p) {
      case MyEnum.A:
        return [
          MyEnum.B,
          MyEnum.C,
        ] as ChildrenAllowed<T>[]; // here
      case MyEnum.B:
        return [
          MyEnum.C,
        ] as ChildrenAllowed<T>[]; // here
      default:
        return [] as ChildrenAllowed<T>[]; // here
    }
  };
  • Related