交叉类型(Intersection Types)
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable
同时是 Person
和 Serializable
和 Loggable
。 就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子:
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
联合类型(Union Types)
联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 number
或 string
类型的参数。 例如下面的函数:
// any类型
function padLeft(value: string, padding: any) { }
// 这里采用联合类型,对类型限制为string和number
function padLeft(value: string, padding: string | number) {}
类型保护与区分类型(Type Guards and Differentiating Types)
对于联合类型,也可包括自定义类型, 像这种情况调用 Bird.swim()
就会报错, 如何区分呢?
// 联合类型也可以包括自定义类型
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(): Fish | Bird {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim(); // errors
用户自定义的类型保护
用自定义方法判断类型差别,自行编码实现.
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
typeof
类型保护
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
instanceof类型保护
// 联合类型也可以包括自定义类型
interface Bird {
fly();
layEggs();
}
interface Fish {
swim();
layEggs();
}
function getSmallPet(obj): Fish | Bird {
// ...
return obj;
}
class Dock implements Bird {
fly(){
console.log('dock fly...');
}
layEggs(){
console.log('dock layeggs...');
}
}
var dark = new Dock();
let pet = getSmallPet(dark);
pet.layEggs(); // okay
if(dark instanceof Dock){
dark.fly(); // errors
}
null和undefined
TypeScript具有两种特殊的类型, null
和 undefined
,它们分别具有值null和undefined.
注意,按照JavaScript的语义,TypeScript会把 null
和 undefined
区别对待。 string | null
, string | undefined
和 string | undefined | null
是不同的类型。
可选参数和可选属性
使用了 --strictNullChecks
,可选参数会被自动地加上 | undefined
:
function f(x: number, y?: number) {
return x + (y || 0);
}
f(1, 2);
f(1);
f(1, undefined); // 默认认为可选参数为undefinded; 在ts中这和null是有区别的
f(1, null); // error, 'null' is not assignable to 'number | undefined'
// class C
class C {
a: number;
b?: number;
}
let c = new C();
c.a = 12;
c.a = undefined; // error, 'undefined' is not assignable to 'number'
c.b = 13;
c.b = undefined; // ok
c.b = null; // error, 'null' is not assignable to 'number | undefined'
语法!
如果编译器不能够去除 null
或 undefined
,你可以使用类型断言手动去除。 语法是添加 !
后缀:
interface UserI{
name: string;
}
function foo(obj?: UserI){
// 这里如果没有! 会报错误提示 obj可能为undefined
const name = obj!.name;
console.log(name);
}
foo();
type和interface
type称为类型别名, 是给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
// 起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用
type Name = string; // 没有意义
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}
像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。
共同点:
type和interface都可以描述一个对象或者函数.
interface User {
name: string;
age: number;
}
interface setAge{
(age: number): number;
}
type User1 = {
name: string;
age: number;
}
type setAge1 = (age: number) => number;
区别1:
拓展(extends)与 交叉类型(Intersection Types) .
interface 可以 extends, 但 type 是不允许 extends 和 implement 的,但是 type 缺可以通过交叉类型 实现 interface 的 extend 行为,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 与 interface 类型 交叉 。
interface extends interface
interface Name {
name: string;
}
interface User extends Name {
age: number;
}
const zs: User = {
name: '张三',
age: 30
}
interface extends type
type Name = {
name: string;
}
interface User extends Name {
age: number;
}
const zs: User = {
name: '张三',
age: 30
}
console.log(zs);;
type 与 type 联合
type Name = {
name: string;
}
type User = Name & { age: number };
const zs: User = {
name: '张三',
age: 30
}
console.log(zs);;
type 与 interface 交叉
interface Name {
name: string;
}
type User = Name & {
age: number;
}
const zs: User = {
name: '张三',
age: 30
}
console.log(zs);;
区别2:
type 可以声明基本类型别名,联合类型,元组等类型,但interface不行.
// 基本类型别名
type Name = string; // 虽然没有实质性作用
// 联合类型
type Foo = number | string;
// typeof赋值
interface Person {
name: string;
age: number;
}
const sem: Person = { name: "semlinker", age: 30}
type Sem = typeof sem; // type Sem = Person
总结:
最重要区别是类型别名不能被 extends
和 implements
(自己也不能 extends
和 implements
其它类型)。 因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。当然如果只是定义一个数据类型的话,type和interface是 一样的.
其他
其他一些高级复杂的用法,实际开发碰到再查阅相关资料.
这里列列举的只是在常规开发中,很大可能性会碰到的,必须先知道的知识点.