首页 > 解决方案 > Typescript 基于嵌套成员推断父类型

问题描述

Typescript 可以根据您在 if 语句中提出的问题来推断值的类型。例如,一个对象的一个​​成员可以基于另一个成员来推断:

type ChildType = 'a' | 'b';

type Child<T extends ChildType> = T extends 'a' ? { type: 'a', color: 'blue' } : T extends 'b' ? { type: 'b', color: 'red' } : never;

interface Parent<T extends Child<ChildType>> {
    child: T extends Child<infer R> ? Child<R> : never;
}


function test<T extends Parent<Child<any>>>(parent: T) {
    if (parent.child.type === 'a') {
        parent.child.color === 'red'; // complains because should be blue
    }
}

但是,使用类型的嵌套成员执行相同的检查,它似乎并没有提供相同的效果。

type ChildType = 'a' | 'b';

interface BaseParent<T extends Child<ChildType>> {
    child: T;
}

interface Child<T extends ChildType> {
    type: T;
}

type Parent<T extends ChildType>
    = T extends 'a' ? { color: 'blue' } & BaseParent<Child<'a'>>
    : T extends 'b' ? { color: 'red', opacity: 2 } & BaseParent<Child<'b'>>
    : never;

function test<T extends Parent<ChildType>>(parent: T) {
    if (parent.child.type === 'a') {
        parent.color === 'red' // should complain, but doesn't
    }
}

这可以很容易地使用类型保护来纠正,但是我正在寻找一种没有它们的方法。

标签: typescript

解决方案


首先要做的事情 - 让父数据依赖于它的子数据可能不是正确的架构。依赖关系应该下降。你可以先考虑一下。

但是,如果您愿意,我会这样做(我添加了动物示例以使我更容易理解):


type AcceptedAnimal = 'dogs' | 'cats';

interface ParentBase {
  accepts: AcceptedAnimal
}

interface Cat {
  meows: boolean;
}

interface Dog {
  race: string;
}

type Parent = DogOwner | CatOwner;

interface DogOwner extends ParentBase {
  animal: Dog
}

interface CatOwner extends ParentBase {
  animal: Cat;
}

function isDogOwner(parent: ParentBase): parent is DogOwner {
  return parent.accepts === 'dogs';
} 

function test(parent: Parent) {
  if (isDogOwner(parent)) {
    parent.animal.race; // ok
    return;
  }

  parent.animal.meows; // ok;
}

推荐阅读