Angular 学习笔记 (Typescript 高级篇)
由于 typescript 越来越复杂. 所以特意开多一个篇幅来记入一些比较难的, 和一些到了一定程度需要知道的基础.
主要参考
https://basarat.gitbook.io/typescript/ 高级书
https://jkchao.github.io/typescript-book-chinese/ 高级书中文版
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 高级篇)的更多相关文章
- Asp.net core Identity + identity server + angular 学习笔记 (第五篇)
ABAC (Attribute Based Access Control) 基于属性得权限管理. 属性就是 key and value 表达力非常得强. 我们可以用 key = role value ...
- Asp.net core Identity + identity server + angular 学习笔记 (第四篇)
来说说 RBAC (role based access control) 这是目前全世界最通用的权限管理机制, 当然使用率高并不是说它最好. 它也有很多局限的. 我们来讲讲最简单的 role base ...
- Asp.net core Identity + identity server + angular 学习笔记 (第三篇)
register -> login 讲了 我们来讲讲 forgot password -> reset password 和 change password 吧 先来 forgot pa ...
- angular学习笔记(三十一)-$location(2)
之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1). 这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等. *注意,这里介绍 ...
- angular学习笔记(三十一)-$location(1)
本篇介绍angular中的$location服务的基本用法,下一篇介绍它的复杂的用法. $location服务的主要作用是用于获取当前url以及改变当前的url,并且存入历史记录. 一. 获取url的 ...
- angular学习笔记(三十)-指令(10)-require和controller
本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...
- angular学习笔记(三十)-指令(7)-compile和link(2)
继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...
- angular学习笔记(三十)-指令(7)-compile和link(1)
这篇主要讲解指令中的compile,以及它和link的微妙的关系. link函数在之前已经讲过了,而compile函数,它和link函数是不能共存的,如果定义了compile属性又定义link属性,那 ...
- angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令
在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...
- angular学习笔记(三十)-指令(5)-link
这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...
随机推荐
- 解决方案 | pywintypes.com_error: (-2147418111, '被呼叫方拒绝接收呼叫。', None, None)
解决方案:加一个time.sleep(1)即可
- django信号中的条件判断不符合时如何提示错误并返回
在Django中,如果你在信号(Signal)处理函数中需要进行条件判断,如果条件不符合,你可以触发一个异常,并在视图或其他地方捕获这个异常,然后返回相应的错误提示. 以下是一个简单的例子,演示如何在 ...
- oeasy教您玩转vim - 26 - 缩进设置
缩进设置 回忆上节课内容 这次了解了颜色的细节 设置 256 色模式 :set t_Co=256 然后确定了具体的各种颜色 还可以生成网页 :TOhtml 还有什么好玩的么? 缩进设置 在 ...
- oeasy教您玩转vim - 37 - # 删除字符
通过十进制的 ascii 值输入字符 在输入模式下 输入 ctrl + v 然后再输入 065 通过十六进制的 unicode 在输入模式下 输入 ctrl + v 然后再输入 u2642 就可以 ...
- Python 正则表达式实战之Java日志解析
需求描述 基于生产监控告警需求,需要对Java日志进行解析,提取相关信息,作为告警通知消息的内容部分. 提取思路 具体怎么提取,提取哪些内容呢?这里笔者分析了大量不同形态的生产日志,最后总结出4种形态 ...
- BTC 地址
比特币地址(Bitcoin Address)是用于接收和发送比特币的唯一标识符,类似于传统金融系统中的银行账号.一个比特币地址由一串字母和数字组成,通常以1.3或bc1开头,具体长度为26至35个字符 ...
- Java工具库——Hutool的常用方法
Hutool-All(或简称Hutool)是一个功能强大的Java编程工具库,旨在简化Java应用程序的开发. 它提供了大量的工具类和方法,涵盖了各种常见任务,包括字符串处理.日期时间操作.文件操作. ...
- CentOS安装RustDesk自建服务器
一.安装node.js 1.下载并解压 [root@VM-8-2-centos src]# cd /usr/local # 下载node文件 [root@VM-8-2-centos local]# w ...
- 【VMware VCF】VMware Cloud Foundation Part 06:部署 VI 工作负载域。
VMware Cloud Foundation 标准架构中,管理域和 VI 工作负载域需要分开部署,管理域是初始构建(Bring-up)中部署的一个工作负载域并且只有一个,管理域专门用于承载管理相关组 ...
- 【DataBase】MySQL 05 基础查询
MySQL数据库 05 基础查询 视频参考自:P18 - P27 https://www.bilibili.com/video/BV1xW411u7ax 配套的SQL脚本:https://shimo. ...