TypeScript中的怪语法如何处理undefined 和 null
undefined的含义是:一个变量没有初始化。null的含义是:一个变量的值是空。
undefined 和 null 的最佳实践核心思想: 避免null pointer错误。null is bad。
要避免这个问题,我们需要做到:
用undefined,不要用null。根据Code guidelines from Microsoft。
Enable "strict" 或者 "strictNullChecks" 编译选项在tsconfig.js中:
{ "compilerOptions": { "strict": true, //... }}
对于不可能是null的变量:声明不能为 null 和 undefined。提示编译错误:当使用一个没有初始化的变量,而这个变量不能为undefined的时候。提示编译错误:当给一个不能为 null 和 undefined 的变量,赋值 null 和 undefined 的时候。如果使用了"strictNullChecks" 编译选项,TypeScript编译器默认任何变量都不能为 undefined 和 null。除非显式声明。
var name: string; // cannot be null and undefined.name = undefined; // Error: [ts] Type 'undefined' is not assignable to type 'string'.name = null; // Error: [ts] Type 'null' is not assignable to type 'string'.console.log(name); // Error: [ts] Variable 'address' is used before being assigned.
对于可能是undefined的变量:使用显式声明提示编译错误:当使用一个可能为null的变量的时候。使用前,需要确定不是undefined.
var address: string | undefined; // can be undefinedclass Person { name: string; // cannot be null and undefined address?: string; // can be undefined}var person : Person = {name: "Joe"};console.log(person.address.toString()); // Error: [ts] Object is possibly 'undefined'.if (person.address != undefined) { console.log(person.address.toString()); //Ok. as we checked the type}
Index Type Query – keyof
keyof 定义了一个Type, 这个Type的值来自于指定的类。
class Person { id: number; name: string; birthday: Date;}type personPropKeys = keyof Person; // same as: type personPropKeys = "id" | "name" | "birthday"var propKey : personPropKeys;propKey = "id"; // OKpropKey = "name"; // OKpropKey = "age"; // Error: [ts] Type '"age"' is not assignable to type '"id" | "name" | "birthday"'.
用途 – 生成类的映射类型 – Mapped Typeskeyof的用途是很有趣的。比如:我们希望一个ReadOnlyPerson类,这个类和类Person的属性相同,不过其中每个属性都是只读的。TypeScript使用了keyof提供了下面的类:
// Keep types the same, but make each property to be read-only.type Readonly<T> = { readonly [P in keyof T]: T[P];};// Same property names, but make the value a promise instead of a concrete onetype Deferred<T> = { [P in keyof T]: Promise<T[P]>;};// Wrap proxies around properties of Ttype Proxify<T> = { [P in keyof T]: { get(): T[P]; set(v: T[P]): void }};
类的参数属性 – parameter properties
class Person { // same as to define instance fields: id, name, age constructor(private id: number, public name: string, readonly age: number) { } get Id() : number { return this.id; }}var person = new Person(1, "Mary", 14);console.log(person.name);
Type: {new(): T}
{new(): T}
的主要功能是让通用方法可以创建通用类型的对象。
但是,这个故事有点长。
实现方法1:通过一个方法。
// This is a generic method to create an objectfunction createObject<T>(name:string, creator: (arg: string) => T) : T { return creator(name);}// now we have a class Person, we want to create it via function createObjectclass Person { public constructor(name: string) { this.name = name; } name: string;}// we have to define a creator functionfunction createPerson(name: string): Person { return new Person(name);}// at end, we can create a personvar person = createObject<Person>("Kate", createPerson);
实现方法2:使用构造方法。但是行不通。但是,对象的创建者的主角是构造对象constructor。专门定义一个creator方法也很别扭。我们希望写成的代码是这样的,但是有一个编译错误。
没有研究过为什么这样写行不通。可能是在转义js上有一些问题。
// This is a generic method to create an objectfunction createObject<T>(name:string) : T { return new T(name); // Error: [ts] 'T' only refers to a type, but is being used as a value here.}// now we have a class Person, we want to create it via function createObjectclass Person { public constructor(name: string) { this.name = name; } name: string;}// at end, we can create a personvar person = createObject<Person>("Kate");
实现方法3:使用构造方法类型。结合以上的方法,TypeScript提供了一个新的方式。“`ts
// This is a generic method to create an objectfunction createObject(name:string, creator: {new(name: string): T}) : T {return new creator(name);}
// now we have a class Person, we want to create it via function createObjectclass Person {public constructor(name: string) {this.name = name;}
name: string;
}
// at end, we can create a personvar person = createObject("Kate", Person);
console.log(person);
“`
更多的解释
{new(): T}
的类型是一个 Type,因此可以用于定义变量和参数。
new()
是描述构造函数的签名。所以在new()
中,也定义参数。比如:{new(name: string): T}
。{new(): T}
定义了一个返回类型为 T 的构造函数的Type。
type NewObject<T> = {new(name: string): T}; // type NewPersonType = new (name: string) => Personvar newPersonType: NewObject<Person> = Person;var person2 = new newPersonType("Joe");// we also can write like this, as {} is the root class of object type.type ObjectEmpty = {new(): {}}; // type ObjectEmpty = new () => {}
剩余语法剩余参数 – Rest parameters
function restFunction(first: string, second: string, ...args: string[]): void { console.log(args); // [ 'three', 'four' ]}restFunction("one", "two", "three", "four");
对象传播 – Object Spread and Rest
// shadow copyvar objCopy: any = {...obj};console.log(objCopy); // { x: 1, y: 'name', z: 2 }console.log(objCopy === obj); // false// copy and changevar obj2 = {a: "age"};objCopy = {...obj, z: "zoo"};console.log(objCopy); // { x: 1, y: 'name', z: 'zoo' }// mergevar obj2 = {a: "age"};objCopy = {...obj, ...obj2};console.log(objCopy); // { x: 1, y: 'name', z: 2, a: 'age' }// copy and removelet {z, ...objCopy2} = objconsole.log(objCopy2); // { x: 1, y: 'name' }
在旅途中,我遇见了你,你我相识是缘分!