TypeScript中的怪語法


TypeScript中的怪語法

如何處理undefined 和 null

undefined的含義是:一個變量沒有初始化。
null的含義是:一個變量的值是空。

undefined 和 null 的最佳實踐

  • 核心思想: 避免null pointer錯誤。
    null is bad

要避免這個問題,我們需要做到:

{
    "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 undefined

class 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";     // OK
propKey = "name";   // OK
propKey = "age";    // Error: [ts] Type '"age"' is not assignable to type '"id" | "name" | "birthday"'.
  • 用途 - 生成類的映射類型 - Mapped Types
    keyof的用途是很有趣的。比如:我們希望一個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 one
type Deferred<T> = {
    [P in keyof T]: Promise<T[P]>;
};

// Wrap proxies around properties of T
type 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 object
function 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 createObject
class Person {
    public constructor(name: string) {
        this.name = name;
    }

    name: string;
}

// we have to define a creator function
function createPerson(name: string): Person {
    return new Person(name);
}

// at end, we can create a person
var person = createObject<Person>("Kate", createPerson);

  • 實現方法2:使用構造方法。但是行不通。
    但是,對象的創建者的主角是構造對象constructor。
    專門定義一個creator方法也很別扭。
    我們希望寫成的代碼是這樣的,但是有一個編譯錯誤。

沒有研究過為什么這樣寫行不通。可能是在轉義js上有一些問題。


// This is a generic method to create an object
function 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 createObject
class Person {
    public constructor(name: string) {
        this.name = name;
    }

    name: string;
}

// at end, we can create a person
var person = createObject<Person>("Kate");

  • 實現方法3:使用構造方法類型。
    結合以上的方法,TypeScript提供了一個新的方式。

// This is a generic method to create an object
function createObject<T>(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 createObject
class Person {
    public constructor(name: string) {
        this.name = name;
    }

    name: string;
}

// at end, we can create a person
var person = createObject<Person>("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) => Person
var 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 copy
var objCopy: any = {...obj};
console.log(objCopy);       // { x: 1, y: 'name', z: 2 }
console.log(objCopy === obj);   // false

// copy and change
var obj2 = {a: "age"};
objCopy = {...obj, z: "zoo"};
console.log(objCopy);       // { x: 1, y: 'name', z: 'zoo' }

// merge
var obj2 = {a: "age"};
objCopy = {...obj, ...obj2};
console.log(objCopy);       // { x: 1, y: 'name', z: 2, a: 'age' }

// copy and remove
let {z, ...objCopy2} = obj
console.log(objCopy2);      // { x: 1, y: 'name' }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM