由于 typescript 越来越复杂. 所以特意开多一个篇幅来记入一些比较难的, 和一些到了一定程度需要知道的基础.

主要参考

https://basarat.gitbook.io/typescript/ 高级书

https://jkchao.github.io/typescript-book-chinese/ 高级书中文版

版本 feature

字节前端的 typescript (它掘金几篇都写的不错)

字节前端的 typescript (它掘金几篇都写的不错)

1. 名词术语

Basic Annotation 是基本注解 let a : string

Inline Type Annotation 是内联类型注解 let a : { name: string }

Union 是联合类型 string | number

intersection 是交叉类型 string & number

Tuple 是 元组类型[string, number]

Nullish Coalescing 是 ??

Optional Chaining 是可选链 obj?.name

Rest parameter 是  call(...args : string[])

Spread operator 是 call(...['a', 'b'])

Conditional 是 T extends string ? number : never;

Type Guard 是 类型保护 arg is Foo

Destructuring 是解构 const { name } = { name: 'Derrick' }

Naked type 是 type Naked<T> = T extends ... (没有被任何东西包装)

NotNaked type 是 type NotNaked<T> = { o: T } extends ... // 在对象里面, 算被抱着了

Utility Types 是 Partial, Required, 等这些东西

Mapped types 是 { [P in TKeys] : any }

object literals 是 { name: string }

2. Number Enums as flags

flags 这个概念 c# 也有 (点这里这里), 经常看到 enum 可以配合 | 来用, 比如 :

PropertyInfo[] attrs = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); //获取attrs

简单理解就是把一个 enum 变成 类似 enum list

typescript 也允许我们做到这点. 点这里

首先定义 enum 的时候需要多写一些号码和符号进去进去 (这里用到的是二进制的移位方法, 我不太懂)

enum Abc {
A = 0,
B = 1 << 0,
C = 1 << 1,
D = 1 << 2,
E = 1 << 3
}

然后呢,就可以这样子去使用了.

let x: Abc = Abc.A | Abc.B;
if(x & Abc.B) {
console.log('has B');
}
const hasB = !!(x & Abc.B); // true
const hasC = !!(x & Abc.C); // false

书里有写它的原理,和如果添加移除,但我目前没有使用到,所以就不研究了.

2.1 扩展特定 enum 方法

通过 namespace

enum Abc {
A
}
namespace Abc {
export function print(): string {
return 'dada';
}
}
Abc.print()

3. 理解 target 和 lib 点这里

target es6 的话, lib 就会有 promise 这些东西.

有些时候我们想分开来管理.

target 指的是 output 出来的 js 等级

比如 es5, 那么它就不会有 Promise, Set, Map 之类的.

lib 是我们开发环境用的. 我们当然希望什么都有咯.

所以一个比较合理的做法就是,

target es5 出来的 js 是 es5

lib dom, es6, 开发的时候我们用的 es6 的 js

然后通过 Polyfill 来补上就好了.

4. declare overload method 点这里

没有 overload 的情况下可以这样 declare, 或者用 interface 也可以

type LongHand = {
(a: number): number;
};
type ShortHand = (a: number) => number;

overload 情况下只可以用第一种

type LongHandAllowsOverloadDeclarations = {
(a: number): number;
(a: string): string;
};

5. 兼容性和变体 点这里

兼容性是指一个类型是否可以当另一个类型来使用 (有时候过多的限制会让代码很难写,所以哪怕是强类型我们也经常会要强转之类的, 所以不可以档死了)

对象结构

type IsExtends<T, U> = T extends B ? true : false;
type A = { age: number };
type B = { age: number, name: string }; // 属性可以多, 不可以少
type result1 = IsExtends<B, A>; // true

函数参数

type Method = (age: number) => void;
type Method2 = () => void; // 参数可以少, 不可以多
type result2 = IsExtends<Method2, Method>; // true

上面都是比较简单的例子,只是数量上的问题

复杂的情况是参数类型,返回类型这些.

比如 2 个方法对比, 第 1 个参数类型是子类, 第 2 个是父类, 这个能兼容吗 ?

说到这里可能就需要引入变体的概念了

点这里

首先讲一下定义

A ≼ B 意味着 A 是 B 的子类型。

A → B 指的是以 A 为参数类型,以 B 为返回值类型的函数类型。

协变(Covariant):只在同一个方向;

逆变(Contravariant):只在相反的方向;

双向协变(Bivariant):包括同一个方向和不同方向 (通常这个是不安全的,但有时候没有办法就比如强转一样)

不变(Invariant):如果类型不完全相同,则它们是不兼容的。

来一个函数类型的例子, 2个函数是否可兼容依据参数类型和返回类型

灰狗 ≼ 狗 ≼ 动物

有一个方法是 狗 → 狗

那么怎样的方法是和它兼容的 ?

1. 灰狗 → 灰狗

2. 灰狗 → 动物

3. 动物 → 动物

4. 动物 → 灰狗 (正解)

参数类型默认情况下是双向的 (开启 strictFunctionTypes 之后就换成逆变),也就是说,只要是父类或者子类都 ok,返回类型是协变的,所以 a extends b 的话, a 的返回子类是 ok 的.

没开启 strictFunctionTypes 的时候

type methodParent = (p: Parent) => Parent;
type methodChild = (c: Child) => Child;
type result = IsExtends<methodChild, methodParent>; // true

开启之后就不行了, 改成父类就 ok

type methodParent = (p: Parent) => Parent;
type methodChild = (c: Parent) => Child;
type result = IsExtends<methodChild, methodParent>; // true

为什么参数是逆变的, 而返回值是协变的呢 ?

继承的概念就是我可以用 B 去替代 A. A 方法可以跑的地方,我全部换成 B 方法,它也要可以跑.

假设方法 A 的参数是狗 (要求抽象), 那么调用 A 方法的时候就有可能传入狗或者灰狗 (要求抽象, 但也可以传入具体).

如果 B 方法的参数是灰狗 (要求具体), 而我拿 B 替换了 A, 当调用的人传入狗的时候就不 ok 了 (因为 A 只要求抽象,所以调用的人可能传入抽象, 但是 B 内部却需要具体, 所以直接翻车).

所以参数是逆变的. (参数需要更抽象才行)

假设返回值 A 是狗 (给予具体), 那么获取返回值之后使用的就是具体属性.

如果返回值 B 是动物 (给予更抽象), 那么就翻车了咯

所以返回值是协变的 (返回值要更具体)

协变就是说要求 A 类的地方可以用 A 或 A 的子类来替换 (不可以用 A 的父类), 更具体

逆变就是要求 A 类的地方可以用 A 或 A 的父类来替换 (不可以用 A 的子类). 更抽象

双向就是上面 2 个都可以

不变就是 A 只能用 A 不可以是其它的

6. Conditional, Infer

refer : https://fettblog.eu/typescript-union-to-intersection/ (联合类型转交叉类型)

当 contional 遇到 union (Distributive Conditional Types)

如果是 naked 的话会被拆成多个来解析, not naked 的话就是把 T 传过去用而已

type Naked<T> = T extends any ? { name: T } : never;
type result = Naked<'a' | 'b'>; // { name: 'a' } | { name: 'b' } type NotNaked<T> = T[] extends any ? { name: T } : never;
type result2 = NotNaked<'a' | 'b'>; // { name: 'a' | 'b' }

Infer 有个特色就是如果 infer 是用在参数类型上, 由于是逆变 position, 最终出来的结果会变成交叉类型.

如果不是逆变的话,出来的结果会是联合类型

// 这是转为联合类型
type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;
type T10 = Foo<{ a: string, b: string }>; // string
type T11 = Foo<{ a: string, b: number }>; // string | number
// 这是转为交叉类型
type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string
type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number

这几招通常用在转换 union to array, union to intersection 等

Angular 学习笔记 (Typescript 高级篇)的更多相关文章

  1. Asp.net core Identity + identity server + angular 学习笔记 (第五篇)

    ABAC (Attribute Based Access Control) 基于属性得权限管理. 属性就是 key and value 表达力非常得强. 我们可以用 key = role value ...

  2. Asp.net core Identity + identity server + angular 学习笔记 (第四篇)

    来说说 RBAC (role based access control) 这是目前全世界最通用的权限管理机制, 当然使用率高并不是说它最好. 它也有很多局限的. 我们来讲讲最简单的 role base ...

  3. Asp.net core Identity + identity server + angular 学习笔记 (第三篇)

    register -> login 讲了 我们来讲讲 forgot password -> reset password  和 change password 吧 先来 forgot pa ...

  4. angular学习笔记(三十一)-$location(2)

    之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1). 这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等. *注意,这里介绍 ...

  5. angular学习笔记(三十一)-$location(1)

    本篇介绍angular中的$location服务的基本用法,下一篇介绍它的复杂的用法. $location服务的主要作用是用于获取当前url以及改变当前的url,并且存入历史记录. 一. 获取url的 ...

  6. angular学习笔记(三十)-指令(10)-require和controller

    本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...

  7. angular学习笔记(三十)-指令(7)-compile和link(2)

    继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...

  8. angular学习笔记(三十)-指令(7)-compile和link(1)

    这篇主要讲解指令中的compile,以及它和link的微妙的关系. link函数在之前已经讲过了,而compile函数,它和link函数是不能共存的,如果定义了compile属性又定义link属性,那 ...

  9. angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令

    在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...

  10. angular学习笔记(三十)-指令(5)-link

    这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...

随机推荐

  1. Known框架实战演练——进销存数据结构

    系统主要包含商品信息.商业伙伴(客户.供应商)信息.业务单表头信息.业务单表体信息.对账单表头信息.对账单表体信息. 1. 商品信息(JxGoods) 该表用于存储公司商品信息. 名称 代码 类型 长 ...

  2. 调试 Node.js

    调试 Node.js 调试器 调试器是一种软件工具,用于通过分析方法观察和控制程序的执行流 设计目标:帮助找出 bug 的根本原因,并帮助你解决它 工作方式:将程序托管在自己的执行进程中或者作为附加到 ...

  3. appium+python自动化-文本(name)定位

    前言 appium1.5以下老的版本是可以通过name定位的,新版本从1.5以后都不支持name定位了 name定位报错 1.最新版appium V1.7用name定位,报错: selenium.co ...

  4. 解决 IIS Express 启动错误:“拒绝访问”问题

    报错 Starting IIS Express ... stderr: Failed to register URL "http://localhost:8378/" for si ...

  5. Jmeter函数助手11-BeanShell

    BeanShell函数用于简单的计算或者运行编程脚本. 表达式求值:填入脚本代码或脚本文件${__BeanShell(source("test.bsh"))} 存储结果的变量名(可 ...

  6. MapGIS路网数据发布

    准备 1.MapGIS 10 桌面版(我用的10.5.6.10) 2.路网的shp文件 数据导入 1.创建要素集,如果已有要素集可以不用创建: 2.导入路网要素类,选择准备好的shp文件后导入即可: ...

  7. 【MongoDB】Re04 副本集 ReplicationSet

    MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务. 副本集可提供冗余和高 可用性,是所有生产部署的基础. 也可以说,副本集类似于有自动故障恢复功能的主从集群.通 ...

  8. Springboot实现HTML表单from简单的接收信息

    HTML< from >元素 from可向Web服务器提交请求 普遍格式: <from action="服务器地址" method="请求方式" ...

  9. 在计算机论文中suppose suggest assume 用法上的区别

    ChatGPT3.5的答案: 在计算机论文中,"suppose," "suggest," 和 "assume" 有不同的用法和含义.它们在表 ...

  10. python报错:ImportError: cannot import name 'Literal' from 'typing'

    原因: Literal 只支持python3.8版本以上的环境,需要把python3.7升级到3.8版本以上. 参考: https://blog.csdn.net/yuhaix/article/det ...