如何使Typescript要求在对象中定义另一个属性之前设置属性的值

r1zhe5dt  于 2022-12-27  发布在  TypeScript
关注(0)|答案(2)|浏览(105)

我需要定义一个类型,以便只接受这样的对象:如果同一对象中的另一个属性定义了值,则可以用值定义该属性。
我为我的需求尝试了下面的类型定义,它是最接近我想要的。

type TypeA = {
  width?: number;
  height?: number;
  position: string;
  innerSize?: string;
} | {
  width?: number;
  height?: number;
  position?: undefined;
}

接收该类型对象的函数需要检查属性position是否已定义,然后才能使用innerSize,这很酷。
但是,在创建这种类型的对象时,我遇到了意想不到的事情:

function test() {
  const a: TypeA = {};
  const b: TypeA = { width: 50, height: 75 };
  const c: TypeA = { position: "5, 7" };

  // Type '{ innerSize: string; }' is not assignable to type 'TypeA'.
  // Property 'position' is missing in type '{ innerSize: string; }' but required in type '{ width?: number | undefined; height?: number | undefined; position: string; innerSize?: string | undefined; }'.
  const d: TypeA = { innerSize: "400, 120" };

  const e: TypeA = { width: 50, innerSize: "400, 120" }; // It compiles!
}

我不能创建变量d,因为Typescript要求定义属性position,但是,我可以通过定义属性width来绕过这个限制......那么,什么会产生这样的结果呢?

myzjeezk

myzjeezk1#

{ width: 50, innerSize: "400, 120" }

此对象在技术上与TypeA兼容,因为它与联合的以下部分匹配:

{
  width?: number;
  height?: number;
  position?: undefined;
}

它的宽度是一个数字,它省略了高度和位置,所以它匹配类型。它确实有一个多余的属性innerSize,但是typescript在检查兼容性时允许多余的属性。
尽管如此,这并不是一个完整的解释,因为通常typescript会在你创建对象的那一行检查多余的属性,所以它应该捕捉到这一点,它应该捕捉到,但它没有,因为这是一个已知的(而且长期存在的)bug:https://github.com/microsoft/TypeScript/issues/20863

ep6jt1vc

ep6jt1vc2#

作为一种解决方案,对于某些形状中不能定义的属性,请使用never类型:

type TypeA = {
    width?: number;
    height?: number;
    position: string;
    innerSize?: string;
} | {
    width?: number;
    height?: number;
    position?: undefined;
    innerSize?: never;
}

function test() {
    const a: TypeA = {};
    const b: TypeA = { width: 50, height: 75 };
    const c: TypeA = { position: "5, 7" };
    const c2: TypeA = { position: "5, 7", innerSize: "400, 120" }; // Okay

    // Type '{ innerSize: string; }' is not assignable to type 'TypeA'.
    // Property 'position' is missing in type '{ innerSize: string; }' but required in type '{ width?: number | undefined; height?: number | undefined; position: string; innerSize?: string | undefined; }'.
    const d: TypeA = { innerSize: "400, 120" };
    //    ~

    const e: TypeA = { width: 50, innerSize: "400, 120" }; // Error: Property 'position' is missing in type '{ width: number; innerSize: string; }' but required in type '{ width?: number | undefined; height?: number | undefined; position: string; innerSize?: string | undefined; }'.
    //    ~
}

Playground链接

相关问题