TypeScript 对象定义中的 '!:' 和 '?:' 有啥区别?

Posted

技术标签:

【中文标题】TypeScript 对象定义中的 \'!:\' 和 \'?:\' 有啥区别?【英文标题】:What is the difference Between '!:' and '?:' in TypeScript object definitions?TypeScript 对象定义中的 '!:' 和 '?:' 有什么区别? 【发布时间】:2020-12-10 19:54:10 【问题描述】:
Class Employee 
  firstName: string;
  lastName!: string;
  middleName?: string;

Employee类的这3个不同字段有什么区别?

Live Example

【问题讨论】:

typescriptlang.org/docs/handbook/…, typescriptlang.org/docs/handbook/release-notes/… 为什么不将完整的代码 放入 问题中(并在您处理问题时修复 Class/class 问题)?为什么只在场外? 这能回答你的问题吗? Type ORM '?' meaning in entities 【参考方案1】:

它们很好地隐藏在documentation of TypeScript中。

? 在interfaces 上进行了描述,它标志着optional property。

! 是definite assertion operator。它告诉编译器该属性已设置(不是 nullundefined),即使 TypeScript 的分析无法检测到。


顺便说一句,Class 不是 TypeScript 或 javascript 关键字,会在该位置产生错误。他们声明类的关键字是class。 TypeScript 和 JavaScript 标识符和关键字区分大小写(Classclass 是不同的东西)。

【讨论】:

【参考方案2】:

? 在那个位置marks the property optional。

该位置的! 是definite assignment assertion。它是non-null assertion operator 的声明级版本,但用于属性(也可用于变量)而不是表达式。

该示例中有两个(或者可以说是三个)错误:

    Class 应该是class; JavaScript 和 TypeScript 区分大小写。

    您需要firstName 上的初始化程序(或无条件分配给它的构造函数)。

    lastName 上的 ! 告诉 TypeScript,lastName 肯定会被分配,从而抑制了您为 firstName 获得的那种错误,但实际上(在示例中)没有任何效果 em> 使用 ! 的任务承诺你肯定知道你正在做的 TypeScript。

编辑:code you linked 稍后处理上面的#1 和#2,但不处理#3。 TypeScript 不会警告 lastName 从未被赋值并假定它的值是一个字符串,而实际上它不存在,因此读取它的值将导致 undefined

【讨论】:

lastName!: string;declare lastName: string;有什么区别? @T.J.Crowder you can use declare inside a class 截至 TS3.7 to support class field [[Define]] semantics。 @jcalz - 谢谢!【参考方案3】:

当有编译器选项strictNullChecks: false

如果您的tsconfig.json 中有strictNullChecks: false,那么它们完全相同,因为禁用strictNullChecks 意味着所有字段都可以将空值或未定义为有效值。

当有编译器选项strictNullChecks: true

class Employee 
   firstName: string;
   lastName!: string;
   middleName?: string;

firstName: string 表示firstName 必须是stringnullundefined 不是 firstName 的有效值。

所有未初始化的字段都会有一个默认值undefined,所以这会导致错误Property 'firstName' has no initializer and is not definitely assigned in constructor

要消除错误,您需要将声明更改为 firstName: string = 'Some default value' 或添加构造函数并在构造函数中为其赋值。

constructor() 
    this.firstName = 'some default value';

现在开始!句法。 lastName!: string 语法与 lastName: string 相似,因为它基本上表示 string 是唯一允许的类型。不允许使用 nullundefined。但它会使编译器对明确的赋值错误保持沉默。假设您有以下代码。

   class Employee 
       firstName: string;
       lastName!: string;
       middleName?: string;

      constructor() 
          // This will silence the compiler about first name not initialized
          this.firstName = 'some default value';
          // The compiler cannot tell that lastName is assigned in init() function
          this.init();
      
      
      private init(): void 
          this.lastName = 'some default value';
      
    

在前面的代码中,lastName 肯定是通过this.init() 调用在constructor 中分配的。但是,编译器无法知道这一点。所以添加!基本上是在告诉编译器“闭嘴,我知道我在做什么”。然后由您来确保代码的正确性。

关于middleName?: string 语法。这类似于middleName: string | undefined;由于所有值都有一个默认值undefined,因此编译器不会抱怨middleName 没有被赋值。

【讨论】:

以上是关于TypeScript 对象定义中的 '!:' 和 '?:' 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

将自定义道具传递给 TypeScript 中的 Redux 表单字段

TypeScript中的接口和抽象类

在 Vue 3 Typescript 中使用提供/注入访问组件中的方法,错误对象可能是“未定义”

TypeScript,接口(Interfaces),对象、数组和函数的类型

深入浅出TypeScript- 使用接口和类型别名

Typescript入门手册之引用类型在TypeScript中的应用