如何告诉TypeScript对象中的可选属性是否存在并设置?

ktr*_*yak 7 typescript

我有以下代码:

interface First
{
  propertyA: string;
}

// Here propertyA is optional
// Imagine that this interface came from external library.
interface Second
{
  propertyA ?: string;
}

function fn(arg: First)
{
  // ...
}

// I know that this object can be declared as type of First,
// but I really need set this as type of Second interface
let myVar: Second = {propertyA: 'some string'};

// I really need in this way to make the call.
fn(myVar); // Error

if(myVar.hasOwnProperty('propertyA'))
{
  fn(myVar); // Still same error
}

if(myVar.propertyA)
{
  fn(myVar); // Still same error
}
Run Code Online (Sandbox Code Playgroud)

但TypeScript抛出错误:

"Second"类型的参数不能分配给"First"类型的参数.属性'propertyA'在类型'Second'中是可选的,但在'First'类型中是必需的.

那么,如何告诉打字稿是可选属性propertyAmyVar存在,并且设置?

aac*_*neo 13

老问题,但在较新版本的打字稿中有一个非常干净的解决方案

fn(myVar!);
Run Code Online (Sandbox Code Playgroud)

在打字稿中,什么是!(感叹号/爆炸)取消引用成员时的运算符?

  • `!` 会将 `myVar` 视为非空/未定义,而不是其属性。 (4认同)
  • 对@alex-i,我进行了编辑以澄清!令人惊奇的是我花了三年时间却不知道我回答了其他问题! (2认同)

Nit*_*mer 5

你可以这样做:

fn(myVar as First);
Run Code Online (Sandbox Code Playgroud)

并为 if使用类型保护

function isFirst(obj: any): obj is First {
    return obj && obj.propertyA;
}

if(isFirst(myVar)) {
  fn(myVar);
}
Run Code Online (Sandbox Code Playgroud)


eln*_*ren 5

这个问题可能更一般地是关于创建一个类型保护,该类型保护告诉编译器您的值是一个新类型,其中所述字段不是可选的,而是必填/必需的。

一种方法是使用Required<T>TypeScript附带的类型,该类型翻转所有字段成为必需。但是,更现实的情况是可能不是全部,而是仅检查了某些字段。

这是这种情况下的通用类型和类型保护的示例:

    /** Interface with optional properties */
    interface IOptionalData {
      foo?: { bar?: string };
      other?: { bar?: string};
      always: { bar?: string };
    }

    /** Utility to make certain keys of a type required */
    type RequiredKeys<T, K extends keyof T> = Exclude<T, K> & Required<Pick<T, K>>

    /** Typeguard for property 'foo' in IOptionalData */
    const ensureFooProperty = (data: IOptionalData): data is RequiredKeys<IOptionalData, 'foo'> =>
      !!data.foo && typeof data.foo.bar === 'string'

    const accessData = (data: IOptionalData) => {
      if (ensureFooProperty(data)) {
        console.log(data.always.bar) // always is always defined
        console.log(data.other.bar)  // COMPILER ERROR: 'other' is possibly undefined
        return data.foo.bar          // accessing optional props is allowed due to ensureToFoo
      }
      console.log(data.foo.bar)      // COMPILER ERROR: 'foo' is possibly undefined
    }
Run Code Online (Sandbox Code Playgroud)

https://gist.github.com/elnygren/ddd28c2f0d737d8a1130da783426fea7

注意:在我的示例中,您始终可以将check内联到if语句中,但是,由于DRY,这并非总是最佳的操作方法(您的类型防护可能会更复杂)


Kub*_*oda 2

我不明白为什么你将它声明为类型,Second因为它具有该属性。但是,您可以执行以下操作之一:

  • 将声明中的类型更改为First,即let myVar: First = {propertyA: 'some string'};
  • 完全删除类型声明。然后它将收到一个匿名类型{ propertyA: string; }并可分配给First,即let myVar = {propertyA: 'some string'};
  • 使用显式类型转换,即fn(<First>myVar);

导致该错误的原因是假设存在可选属性是不安全的。