1.交叉类型
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子:
const mergeFunct = <T,U>(obj1:T,obj2:U):T&U =>{
// let res = {} as T & U
let res = <T&U>{}
res = Object.assign(obj1,obj2)
return res
}
mergeFunct({a:'a'},{b:'b'})
2.联合类型
联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 number或 string类型的参数。 例如下面的函数:
type1 | type2 | type3 |type4
const getLengthFun = (context:number | string):number=>{
if(typeof context === 'string'){
return context.length
}else{
return context.toString().length
}
}
3.类型的保护
3.1 typeof 的类型保护
这些typeof类型保护只有两种形式能被识别: typeof v === "typename"和 typeof v !== “typename”, "typename"必须是 “number”, “string”, "boolean"或 “symbol”。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。
const ValueList = ['abc',123]
const getRandomValue = ()=>{
const num = Math.random() * 10
if(num > 5){
return ValueList[0]
}else{
return ValueList[1]
}
}
const item = getRandomValue()
function isString(type:number | string):type is string{ //类型保护基本上就是这样子的函数来约束输入的值
return typeof type === 'string'
}
if(isString(item)){
console.log(item.length)
}else{
console.log(item.toFixed())
}
3.2 instanceof的类型保护
instanceof类型保护是通过构造函数来细化类型的一种方式。 比如
class CressByClass1{
age = 213
}
class CressByClass2{
name = 'asd'
}
function getRandom(){
return Math.random() * 10 < 5 ? new CressByClass1() : new CressByClass2()
}
const item1 = getRandom()
if(item1 instanceof CressByClass1){
console.log(item1.age)
}else{
console.log(item1.name)
}
4.null / undefined
TypeScript具有两种特殊的类型, null和 undefined,它们分别具有值null和undefined.
默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。
let s = "foo";
s = null; // 错误, 'null'不能赋值给'string'
let sn: string | null = "bar";
sn = null; // 可以
sn = undefined; // error, 'undefined'不能赋值给'string | null'
注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。
5.类型断言
function getLengthStr(num:number | null):string{
function gerRes(prefix:string){
return prefix + num!.toFixed().toString()
}
num = num || 0.1
return gerRes('lison-')
}
console.log(getLengthStr(9.39999))
6.类型别名
类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
type TypeString = string;
let str2 : TypeString
type PositionType<T> = {x:T,y:T}
const position1:PositionType<number> = {
x : 1,
y : -1
}
const position2:PositionType<string> = {
x:'x',
y:'y'
}
type Childs<T> = {
current:T,
child?:Childs<T>
}
使用接口也可以达到类型别名的作用
使用接口也可以起到类型别名的作用
interface Alias {
num:number
}
interface Interface{
num:number
}
let _alia:Alias = {
num:1
}
let inter:Interface = {
num:2
}
_alia = inter
7.自变量类型
字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。
type Name = 'Lison'
const name3:Name = 'Lison'
type Direction = 'north' | 'east' | 'south' | 'west'
function getDirectionFirstLetter(direction:Direction){
return direction.substr(0,1)
}
console.log(getDirectionFirstLetter('east'))
type Age = 18;
interface InfoInterface {
name:string;
age:Age
}
const info:InfoInterface = {
name:'aa',
age:18
}
8.可辨识联合
你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做 可辨识联合的高级模式,它也称做 标签联合或 代数数据类型。 可辨识联合在函数式编程很有用处。 一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具有3个要素:
- 具有普通的单例类型属性— 可辨识的特征。
- 一个类型别名包含了那些类型的联合— 联合。
- 此属性上的类型保护。
interface Square {
kind:'square',
size:number
}
interface Rectangle {
kind:'rectangle',
width:number,
height:number
}
interface Circle {
kind:'circle',
radius:number
}
type Shape = Square | Rectangle | Circle
function asserNever(value:never):never {
throw new Error('Unexpected onject' + value)
}
function getArea(s:Shape):number{
switch(s.kind){
case 'square':
return s.size * s.size;
break
case 'rectangle':
return s.height * s.width
break
case 'circle':
return Math.PI * s.radius ** 2
break;
default:
return asserNever(s)
}
}
9.this 类型
this类型表示的是某个包含类或接口的 子类型。 这被称做 F-bounded多态性。 它能很容易的表现连贯接口间的继承。
return this 之后就可以直接链式编程
class Counter {
constructor(public count:number = 0){}
add(value:number){
this.count += value
return this
}
subtract(value:number){
this.count -= value
return this
}
}
const counter = new Counter(10)
console.log(counter.add(90).subtract(77))
class PowCounter extends Counter {
constructor(public count:number = 0){
super(count)
}
pow(value:number){
this.count = this.count ** value
return this
}
}
const powcount = new PowCounter(2)
console.log(powcount.pow(3).add(1).subtract(7))
10.索引类型 keyof
使用索引类型,编译器就能够检查使用了动态属性名的代码。 例如,一个常见的JavaScript模式是从对象中选取属性的子集。
function pluck(o, names) {
return names.map(n => o[n]);
}
下面是如何在TypeScript里使用此函数,通过 索引类型查询和 索引访问操作符:
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
编译器会检查 name是否真的是 Person的一个属性。 本例还引入了几个新的类型操作符。 首先是 keyof T, 索引类型查询操作符。 对于任何类型 T, keyof T的结果为 T上已知的公共属性名的联合。 例如:
let personProps: keyof Person; // 'name' | 'age'
keyof Person是完全可以与 ‘name’ | 'age’互相替换的。 不同的是如果你添加了其它的属性到 Person,例如 address: string,那么 keyof Person会自动变为 ‘name’ | ‘age’ | ‘address’。 你可以在像 pluck函数这类上下文里使用 keyof,因为在使用之前你并不清楚可能出现的属性名。 但编译器会检查你是否传入了正确的属性名给 pluck:
pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'
第二个操作符是 T[K], 索引访问操作符。 在这里,类型语法反映了表达式语法。 这意味着 person[‘name’]具有类型 Person[‘name’] — 在我们的例子里则为 string类型。 然而,就像索引类型查询一样,你可以在普通的上下文里使用 T[K],这正是它的强大所在。 你只要确保类型变量 K extends keyof T就可以了。 例如下面 getProperty函数的例子:
function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
return o[name]; // o[name] is of type T[K]
}
getProperty里的 o: T和 name: K,意味着 o[name]: T[K]。 当你返回 T[K]的结果,编译器会实例化键的真实类型,因此 getProperty的返回值类型会随着你需要的属性改变。
let name: string = getProperty(person, 'name');
let age: number = getProperty(person, 'age');
let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
索引类型和字符串索引签名
keyof和 T[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么 keyof T会是 string。 并且 T[string]为索引签名的类型:
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
11.Readonly Partial Pick Record
Readonly 设置里面的全部为只读 – 系统自带的可以直接使用
interface Info1 {
age:number,
name:string,
sex:string
}
type ReadyonlyType<T> = {
readonly [P in keyof T] : T[P] //这里面的 [P in keyof T] 相当于js 里面的数组的 for 循环 P 就相当于里卖的item.
}
type ReadonlyInfo1 = Readonly<Info1>
let info1:ReadonlyInfo1 = {
age:19,
name:'lison',
sex:'man'
}
Partial 设置全部为可选 – 系统自带的可以直接使用
interface Info1 {
age:number,
name:string,
sex:string
}
type PartialType<T> = {
Partial [P in keyof T] : T[P]
}
type PartialInfo1 = Partial<Info1>
let info1:PartialInfo1 = {
age:19,
name:'lison',
sex:'man'
}
Pick 是取部分值
type Pick<T,K extends keyof T> = {
[P in K] : T[P]
}
interface Info {
name:string,
age:number,
address:string
}
let info2:Info = {
name:'xiao ming',
age:19,
address:'cahng sha'
}
function pick<T , K extends keyof T>(obj:T , keys:K[]):Pick<T , K>{ //Pick 是取部分
const res:any = {};
keys.map(item=>res[item] = obj[item])
return res
}
const addname = pick(info2,['name','address'])
console.log(addname)
Record 这个就是将传入的属性转换为其他值得一种方法
function mapObject<K extends string | number,T ,U>(obj:Record<K,T>,f:(x:T)=>U):Record<K,U>{
const res:any = {};
for(const key in obj){
res[key] = obj[key]
}
return res
}
const names = { 0:'hello',1:'world',2:'bye' }
const lengths = mapObject(names,s=>s.length)
console.log(lengths)
12.映射
一个常见的任务是将一个已知的类型每个属性都变为可选的:
interface PersonPartial {
name?: string;
age?: number;
}
或者我们想要一个只读版本:
interface PersonReadonly {
readonly name: string;
readonly age: number;
}
这在JavaScript里经常出现,TypeScript提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为 readonly类型或可选的。 下面是一些例子:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
像下面这样使用:
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
下面来看看最简单的映射类型和它的组成部分:
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
它的语法与索引签名的语法类型,内部使用了 for … in。 具有三个部分:
类型变量 K,它会依次绑定到每个属性。
1.字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
2.属性的结果类型。
3.在个简单的例子里, Keys是硬编码的的属性名列表并且属性类型永远是 boolean,因此这个映射类型等同于:
type Flags = {
option1: boolean;
option2: boolean;
}
在真正的应用里,可能不同于上面的 Readonly或 Partial。 它们会基于一些已存在的类型,且按照一定的方式转换字段。 这就是 keyof和索引访问类型要做的事情:
type NullablePerson = { [P in keyof Person]: Person[P] | null }
type PartialPerson = { [P in keyof Person]?: Person[P] }
在这些例子里,属性列表是 keyof T且结果类型是 T[P]的变体。 这是使用通用映射类型的一个好模版。 因为这类转换是 同态的,映射只作用于 T的属性而没有其它的。 编译器知道在添加任何新属性之前可以拷贝所有存在的属性修饰符。 例如,假设 Person.name是只读的,那么 Partial.name也将是只读的且为可选的。
13.由映射类型进行推断
type Proxy<T> = {
get():T;
set(value:T):void;
}
type Proxify<T> = {
[P in keyof T]:Proxy<T[P]>
}
function proxify<T>(obj:T):Proxify<T>{
const result = {} as Proxify<T>
for (const key in obj){
result[key] = {
get:() => obj[key],
set:(value) => obj[key] = value
}
}
return result
}
let props = {
name:'lison',
age:20,
}
let porxyProps = proxify(props)
// porxyProps.name.set('wang')
// console.log(porxyProps.name.get())
function unproxify<T>(t:Proxify<T>):T{
const result = {} as T
for(const key in t){
result[key] = t[key].get()
}
return result
}
console.log(unproxify(porxyProps))
元组的映射
type MapToPromise<T> = {
[P in keyof T] : Promise<T[P]>
}
type Tuple = [number,string,boolean]
type promiseTuple = MapToPromise<Tuple>
let tuple1:promiseTuple = [
new Promise((resolve,reject) => resolve(1)),
new Promise((resolve,reject) => resolve('aa')),
new Promise((resolve,reject) => resolve(false))
]
14.unknown
14.1 unknown 是任何类型都可以赋值给他
let value1:unknown;
value1 = 1
value1 = 'as'
14.2 如果没有类型断言和基于控制细化时,unknown 是不可以赋值给任何类型的
let value2:unknown;
let value3:string = value2
value2 = value1 //对象的类型为 "unknown"
14.3 如果没有类型断言和基于控制流细化时,unknown 是不能在它上面进行任何的操作
let value4 : unknown
value4 += 5; //对象的类型为 "unknown"
14.4 unknown 与任何类型组成的交叉类型都会等于其他类型。
type type1 = string & unknown
type type2 = number & unknown
type type3 = unknown & unknown
type type4 = string & string[]
14.5 unknown 与任何类型组成联合类型都会等于 unknown
type type5 = unknown | string
type type6 = unknown | number
type type7 = unknown | any
14.6 nerve 是 unknown的子类型
type type8 = never extends unknown ? true : false
14.7 kerof unknown 等于nerve
type type9 = keyof unknown
14.8 自能对unknown 进行等于和不等于操作,其他的操作是不可以的
value1 === value2
value1 !== value2
14.9 unknown类型的值是不能访问他的属性,作为函数调用和作为实列来声明
let value5:unknown
value5.age
value5()
new value5()
14.10 如果使用映射来遍历unknown ,是不会返回任何的值
type Types1<T> = {
[P in keyof T]:number
}
type type10 = Types1<number>
type type11 = Types1<unknown>
15.条件类型