Typescript学习之高级类型的总结

Typescript学习之高级类型的总结

高级类型包括交叉类型,联合类型,多态的 this类型,索引类型,映射类型。

交叉类型

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,
它包含了所需的所有类型的特性。 
例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 
就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 
在JavaScript里发生这种情况的场合很多!)
function getNewArrays <T extends Array<string>,U extends string>(x:T,y:U):T | U {
  x.push(y)
  return x
}
function mergeFunc<T,U>(data1:T,data2:U):T & U {
  let result = <T & U>{}
  Object.assign(<any>result,data1,data2)
  return result 
}

联合类型

联合类型与交叉类型很有关联,但是使用上却完全不同。 
偶尔你会遇到这种情况,一个代码库希望传入 number或 string类型的参数。
function addNums(a:string,b:number | string):string {
  return a + b
}
类型谓词 is 类型保护与区分类型 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护
interface Brid {
  fly():void;
}
interface Fish {
  swim():void
} 
// 当animal is Fish这个表达式成立时返回true
function isFish(animal:Brid | Fish):animal is Fish { // 类型谓词 is
  return (<Fish>animal).swim !== undefined;
}
let animals:Brid = {
  fly:function() {
    console.log('fly');
    
  }
}
console.log(isFish(animals)); // false
typeof类型保护和instanceof类型保护
class Padder {
  constructor(readonly name :string) {
   this.name = name
  }
}
function myFunc(value:string|number|object):number|undefined|string {
  if(typeof value === 'string') {
    return value.length
  }
  if( value instanceof Padder) {
    return value.name
  }

}
这些* typeof类型保护*只有两种形式能被识别:
typeof v === "typename"和 typeof v !== "typename",
"typename"必须是 "number", "string", "boolean"或 "symbol"。 
但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。
instanceof的右侧要求是一个构造函数,TypeScript将细化为:

此构造函数的 prototype属性的类型,如果它的类型不为 any的话
构造签名所返回的类型的联合
可以为null的类型
TypeScript具有两种特殊的类型, null和 undefined,
它们分别具有值null和undefined. 
默认情况下,类型检查器认为 null与 undefined可以赋值给任何类型。 
null与 undefined是所有其它类型的一个有效值。
这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种情况也不行。
null的发明者,Tony Hoare,称它为 价值亿万美金的错误。
--strictNullChecks标记可以解决此错误:当你声明一个变量时,它不会自动地包含 null或 undefined。 你可以使用联合类型明确的包含它们:
添加 !后缀 如果编译器不能够去除 null或 undefined,你可以使用类型断言手动去除。 语法是添加 !后缀
function setString(str:string|null):string {
  // charAt(int index)方法是一个能够用来检索特定索引下的字符的String实例的方法。
  return str!.charAt(0)
}
类型别名
类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
type customType = string
type customTypeFunc<T> = (args:T)=> T
type customTypes<U> = customType | customTypeFunc<U>
定义一个接口
type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}
接口 vs. 类型别名
像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。

其一,接口创建了一个新的名字,可以在其它任何地方使用。 
类型别名并不创建新名字—比如,错误信息就不会使用别名。
在下面的示例代码里,在编译器中将鼠标悬停在 interfaced上, 
显示它返回的是 Interface,但悬停在 aliased上时,显示的却是对象字面量类型。
type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;
另一个重要区别是类型别名不能被 extends和 implements
自己也不能 extends和 implements其它类型)。 
因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,
你应该尽量去使用接口代替类型别名。
另一方面,如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,
这时通常会使用类型别名。
类型别名的两种命名形式
   //字符串字面量类型  字符串字面量类型还可以用于区分函数重载:
  type Color = "Green" | "Red" | "Blue";
  // 数字字面量类型
  type num = 1 | 2 | 3 | 4 | 5
可辨识联合
你可以合并单例类型,联合类型,类型保护和类型别名来创建一个叫做 可辨识联合的高级模式,
它也称做 标签联合或 代数数据类型。 可辨识联合在函数式编程很有用处。 
一些语言会自动地为你辨识联合;而TypeScript则基于已有的JavaScript模式。 它具有3个要素:
具有普通的单例类型属性可辨识的特征。
一个类型别名包含了那些类型的联合。
此属性上的类型保护。
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}
// 首先我们声明了将要联合的接口。 每个接口都有 kind属性但有不同的字符串字面量类型。
// kind属性称做 可辨识的特征或 标签。 其它的属性则特定于各个接口。 
// 注意,目前各个接口间是没有联系的。 下面我们把它们联合到一起:
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
}

多态的 this类型

多态的 this类型表示的是某个包含类或接口的 子类型。 
这被称做 F-bounded多态性。 它能很容易的表现连贯接口间的继承,
比如。 在计算器的例子里,在每个操作之后都返回 this类型
// 由于这个类使用了 this类型,你可以继承它,新的类可以直接使用之前的方法,不需要做任何的改变。
class BasicCalculator {
    public constructor(protected value: number = 0) { }
    public currentValue(): number {
        return this.value;
    }
    public add(operand: number): this {
        this.value += operand;
        return this;
    }
    public multiply(operand: number): this {
        this.value *= operand;
        return this;
    }
    
}

let v = new BasicCalculator(2)
            .multiply(5)
            .add(1)
            .currentValue();

索引类型

使用索引类型,编译器就能够检查使用了动态属性名的代码。 
例如,一个常见的JavaScript模式是从对象中选取属性的子集。
interface Persons {
  name:string,
  age:number
}
let persons:Persons = {
  name:'A',
  age:18
}
// keyof T, 索引类型查询操作符
// T[K], 索引访问操作符
function getNameses<T,K extends keyof T>(obj:T,values:K[]):T[K][] {
  return values.map(n => obj[n]);
}
let val:string[] = getNameses(persons,['name'])

// 索引类型和字符串索引签名
// 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

映射类型

Typescript提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性
interface Person {
  name:string,
  age:number
}
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,它会依次绑定到每个属性。
// 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
// 属性的结果类型。

结尾

以上就是本文的主要学习内容,有不正确的地方欢迎指正,谢谢!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值