联合类型
表示取值可以为多种类型中的一种。
- 联合类型使用 | 分隔每个类型。
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
function getString(something: string | number): string {
return something.toString();
}
交叉类型
将多个类型合并为一个类型。
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
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();
混合类型
一个例子就是,一个对象可以同时做为函数和对象使用,并带有额外的属性。
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
类型断言
可以用来手动指定一个值的类型。
它没有运行时的影响,只是在编译阶段起作用。
类型断言有两种形式。
//<类型>值
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
//值 as 类型
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
//需要在还不确定类型的时候就访问其中一个类型的属性或方法
function getLength(something: string | number): number {
if (something.length) {
return something.length;
} else {
return something.toString().length;
}
} //报错
//使用类型断
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的
类型推论
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。
let x = 3;
变量x的类型被推断为数字。 这种推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时。
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。
类型兼容性
TypeScript里的类型兼容性是基于结构子类型的。 结构类型是一种只使用其成员来描述类型的方式。
TypeScript结构化类型系统的基本规则是,如果x要兼容y,那么y至少具有与x相同的属性。
interface Named {
name: string;
}
let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: 'Alice', location: 'Seattle' };
x = y;
//检查函数参数时使用相同的规则
function greet(n: Named) {
alert('Hello, ' + n.name);
}
greet(y); // OK
这里要检查y是否能赋值给x,编译器检查x中的每个属性,看是否能在y中也找到对应属性。 在这个例子中, y必须包含名字是name的string类型成员。y满足条件,因此赋值正确。
函数
形参
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error
允许忽略参数。
返回值
let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});
x = y; // OK
y = x; // Error because x() lacks a location proper
枚举
枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的。
enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };
let status = Status.Ready;
status = Color.Green; //error
类
比较两个类类型的对象时,只有实例的成员会被比较。 静态成员和构造函数不在比较的范围内。
class Animal {
feet: number;
constructor(name: string, numFeet: number) { }
}
class Size {
feet: number;
constructor(numFeet: number) { }
}
let a: Animal;
let s: Size;
a = s; //OK
s = a; //OK
tuple类型
tuple是元组类型,很多语言中也有这种数据类型,比如Python、Swift等。
const tInfo: [string, number, number] = ["why", 18, 1.88];
const item1 = tInfo[0]; // why, 并且知道类型是string类型
const item2 = tInfo[1]; // 18, 并且知道类型是number类型
never类型
never类型表示一种从来不会存在的值的类型,有点绕,我们来这样理解:
- 如果一个函数中是一个死循环,那么这个函数会返回东西吗?不会,那么写void类型或者其他类型作为返回值类型都不合适,我们就可以使用never类型。
- 如果一个函数是抛出一个异常,那么这个函数是不是也没有返回值呢?这个时候我们也可以使用never类型。
const loopFunc=()=>{
while(true){
console.log(123)
}
}
const errorFunc=()=>{
throw new Error("error")
}
Symbol类型(js es类型)
在ES5中,如果我们是不可以在对象中添加相同的属性名称的,比如下面的做法:
const person = {
identity: "程序员",
identity: "老师",
}
通常我们的做法是定义两个不同的属性名字:比如identity1和identity2。
但是我们也可以通过symbol来定义相同的名称,因为Symbol函数返回的是不同的值:
const s1 = Symbol("identity");
const s2 = Symbol("identity");
const person = {
[s1]: "程序员",
[s2]: "老师",
};
类型守护
类型守护是一种错误提示机制。
JavaScript 一个常用的方式就是使用 typeof
或者 instanceof
来在运行时检查一个表达式的类型。TypeScript 现在可在 if
区域块中理解这种情况。
var x:any={};
if(typeof x==="string"){
console.log(x.splice(3,1));//提示错误信息
//TS语言服务可以读懂在条件语句中使用typeof的用法,进而推断出变量x一定是string类型,然后告诉我们splice方法不存在于string类型中。
}
类型别名
TS允许使用type
关键字声明类型别名:
type PrimitiveArray=Array<string|number|boolean>
type SelectType=string|number|boolean
type MyNumber=number;
type Callback=()=>void;
Mix-ins (类似多继承)
一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在ECMAScript中实现混合
var calculatorMixin = Base => class extends Base {
calc() {
console.log("calc");
}
};
var randomizerMixin = Base => class extends Base {
randomize() {
console.log("randomize");
}
};
class Foo {
foo(){
console.log("foo");
}
}
class Bar extends calculatorMixin(randomizerMixin(Foo)) {
}
let bar = new Bar();
bar.calc();
bar.randomize();
bar.foo();