欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

angular2 学习笔记 (Typescript)

程序员文章站 2022-07-03 22:06:46
...

 http://realfiction.net/2019/02/03/typescript-type-shenanigans-2-specify-at-least-one-property

keyof, never, Pick, Exclude, Record, T in Keys, {  }[Keys],

Partial 

T extends U ? X : Y,

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

 

 

更新 2019-05-31 

常用的 Pick, Exclude, Omit

refer : https://*.com/questions/48215950/exclude-property-from-type

Omit 在 3.5 后是 default 了

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

class Person { 
    name: string;
    age: number;
}

type keys = Exclude<keyof Person, 'name'>;

const omit: Omit<Person, 'age'> = {
    name: 'name'
}

const y: Pick<Person, 'name'> = {
    name: 'name'
}

还有 enum 

enum OrderStatus {
    Pending = 'Pending',
    WaitForPayment = 'WaitForPayment',
    WaitForShipping = 'WaitForShipping',   
    Completed = 'Completed',
}
const orderStatus: Exclude<OrderStatus, OrderStatus.Completed> = OrderStatus.Pending;

 

 

更新 2018-12-20

使用 mixin 代码

假设某些属性和方法我们会在多个 component 上复用.

step 1 : 定义 Constructor 类型

type Constructor<T = {}> = new (...args: any[]) => T;

step 2 : 定义复用的 interface 

export interface HaveGetNameMethod {
  getName() : string
}

step 3: 定义这个 class 的 constructor 

export type HaveGetNameCtor = Constructor<HaveGetNameMethod>;

step 4: 定义这个 class 的依赖 (通常是依赖注入的服务, 这里随便写而已)

export interface HaveGetNameMethodDependency {
  name: string
}

step 5: 定义 mixin 方法

function MixinGetName<TBase extends Constructor<HaveGetNameMethodDependency>>(Base: TBase) : TBase & HaveGetNameCtor {
  return class extends Base {
    getName() {
      return this.name;
    }
    constructor(...args: any[]) {
      super(...args);
    }
  };
}

传入的 Base 必须实现依赖, 返回 Base & 这个 class, 这个 class 就是 step 3, 它实现了 step 1 的接口

step 6 : 定义我们的 base 组件, 必须满足我们要 exntends 的 class 的依赖

export class BaseComponent {
  constructor(
    public name: string
  ){ }
}

step 6 定义我们要 extends 的 mixin class (这里可以接 combo)

export const MixinComponent: HaveGetNameCtor & typeof BaseComponent = mixinGetName(BaseComponent);

它的类型就是所有的 constructor 加起来. a(b(c(d))) <-- 如此的嵌套组合调用.

step 7 最后就是继承啦 

export class TestMixinComponent extends MixinComponent implements OnInit, HaveGetNameMethod {

  constructor(
  ) { 
    super('das');  
  }

  ngOnInit() {
    console.log(this.getName());
  }

}

implement 所有接口, 在 constructor 提供所有依赖. 这样就可以啦~

注 :

所有依赖都必须使用 public. 

https://github.com/Microsoft/TypeScript/issues/17744

angular aot 有些场景下 life cycle hook 跑步起来哦

https://github.com/angular/angular/issues/19145

 

更新 2018-05-11 

refer : https://blog.mariusschulz.com/2017/05/26/typescript-2-2-mixin-classes

class 动态继承, Mixin Classes

在写 Angular 的时候, component class 经常需要一些大众的功能或者属性. 

要封装这些方法和属性,可以用 2 种方式,一种是 class 继承, 另一种是注入另一个 class 

2 个方法各有各的优势. 

今天主要说说继承 Mixin Classes 

Material 里面有很好的实现,大家可以去看看代码. 

Mixin Classes 是 typescript 的特性之一,比一般的继承灵活一些. 

我们假设有这样一个场景. 

有 AbstractParentAComponent, ChildAComponent, AbstractParentBComponent, ChildBComponent 4 个组件类

ChildA 继承 ParentA, ChildB 继承 ParentB

假如 ChildA 和 ChildB 拥有共同的属性, 我们要如何去封装复用呢?

这就是 Mixin 排上用场的地方 

我们把 A,B 共同点放入 ChildABComponent 类

然后 ChildA extends ChildAB extends ParentA 和 ChildB extends ChildAB extends ParentB

看到了吗, ChildAB 一会儿继承了 ParentA 一会儿又继承 ParentB,这就是灵活的地方了.

 

 

 

 

 

更新 2018-02-04 

对于 null and undefined 

我们都知道 null 是一个很奇葩的东西. 

比如 : 

let a: { name: string } = null; //编辑时通过
console.log(a.name); //运行时报错

任何一个对象都可以是 null or underfined 

所以就有了 a.name 在编辑时不会报错而在运行时报错的情况。

c# 也是这样的。

虽然我们码农对代码意识很高,几乎每次都会很自然而然的避开这种错误的情况但是 "说好的编辑时报错呢 ? "

c# 中我们会这样就规避上述的报错现象 

a?.name。这和 angular template 语法是一样的。表示如果 a 是 null 那么就返回 null. 这样运行时获取的值是 null 也就不会报错了. 

另一种方法是 typescript 才有的, c# 没有. 叫 stricknullcheck = true 

当你设置了这个后 

let a: { name: string } = null;  在编辑时就报错了 

你必须表明清楚 

let a: { name: string } | null = null;

这样才行。

但是这样的代交是 a 由于是 对象或者 null 

在智能提示时 a dot 就不会显示 name 了, 因为它有可能是 null 啊

于是 就有了 感叹号 ! 

console.log( a!.name );

感叹号告诉 typescript 这里的 a 是不可能为 null or underfined 的。所以就 ok 了

 

 

 

 

1.接口奇葩验证

interface Abc
{ 
    name : string
}
function abc(obj : Abc)
{ 

}
let ttc = { name: "adad", age: 12 };
abc(ttc); //no error
abc({ name: "adad", age: 12 }); //error

对象字面量会有特别的检查, 所以一个 no error ,一个 error.

 

2. readonly

const data: string = "xx";

let obj: {
    readonly name : string  
} = {
    name : "keatkeat"
}
obj.name = "tata"; //error 

const for let, var, readonly for object properties.

 

3. 初始化对象时赋值 (v2.1)

class Person
{ 
    constructor(data? : PartialPerson)
    { 
        Object.assign(this, data);
    }
    public name : string
}
type PartialPerson = Partial<Person>;

let person = new Person({
    name : "x"
});

console.log(person.name);

使用到了 v2.1 的特性 keyof, Partial<T>

 

4. async await 

class Person {
  ajaxAsync(): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      setTimeout(() => {
        resolve("data");
      }, 5000);
    });
  }
}

和 c# 类似, c# 中 Task<string> 对应这里的 Promise<string>

(async function () {
  let person = new Person();
  let data = await person.ajaxAsync();
  console.log(data);
  person.ajaxAsync().then(() => {
    console.log(data);
  });  
})()

使用也和 c# 一样, 必须在 async 方法中才可以使用 await 关键字.

当 await 遇上 Promise 就会有反应了, 当然你也是把它当普通 promise 来使用哦. 

捕获错误 :

使用 try catch 来捕获.

async method() 
{ 
  try { 
    let data = await this.ajaxAsync(); 
  }
  catch (error)
  { 
    console.log(error);
  }   
}

不用 try catch 的捕获方式

async method() 
{ 
  let data = await this.ajaxAsync().catch((error) => {
    console.log(error); 
    return "data"; //if error then data should be ? 
  });  
  console.log(data);
}

ajaxAsync 内部可以使用 return Promise.reject("error loh") 或 throw "error loh" 的方式表示出错.

规则 : 

await 关键字在 async 方法中才能使用 

await 调用的方法 必须是 一个 async method 或则是一个返回 Promise 的方法. 

try catch await 3个一起才能捕获错误. 

执行顺序 

class PersonComponent
{ 
    timeout() : Promise<void>
    {     
        return new Promise<void>((resolve) => {          
            setTimeout(() => { 
                console.log("2");
                resolve();
            }, 3000);
        });
    }
    async ngOnInit()
    { 
        await this.timeout();
        console.log("3");
    } 
}

let p = new PersonComponent();
p.ngOnInit();
console.log("1");

由于没有使用 await p.ngOnInit() 所以 console.log("1") 优先执行. 而使用了 await 的 ngOnInit 是正常的. 所以即使 ng2 没使用 await 来调用 ngOnInit 我们也不用担心会有问题^^

 

容易产生的误解 : async & await , Promise , rxjs 

首先 async await 只是让我们的代码好读一些, 它也是使用 Promise 来做的. 

rxjs 比 promise 灵活, 但不像 promise 简单理解, 而大部分的时候我们只需要 promise (async & await), 所以只有当你要用 ”流“ 的概念时才使用 rxjs 

而如果只是要一个异步方法那么请使用 async await / promise 就够了. 

 

5. 扩展原型 extend prototype 

refer : http://*.com/questions/41192986/extending-the-string-class-doesnt-work-in-some-context

declare global {
    interface String {
        test(c: number): string;
    }
    interface Array<T> {
        test(c: number): string;
    }
}


String.prototype.test = function (c : number)
{
    return "abc";
}

Array.prototype.test = function (c : number)
{
    return "";
}
 

export class Extension
{

}

然后在 app.module import 它出来就可以了, 全局定义

import "./@stooges/extension";