基础类型
数字
let count: number = 6;
字符串
let name: string = 'bill';
布尔值
let loading: boolean = true;
any
标识可以是任意类型
let list: any[] = [1, true, 'aaa'];
null 和 undefined
用处不大
void
与 any 类型相反,标识没有任何类型 使用场景是一个函数没有返回值
function print(): void {
console.log('hello');
}
unknown
任何值都能赋值给unknown类型, unknown类型只能赋值给unknown类型和any类型。
never
标识哪些用不存在值的类型, any也不能赋值给never类型
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
obejct
标识非原始类型,一般用于标识对象
declare function create(o:object | null): void;
create({prop: 0});
creatr(null);
通常会使用interface更加详细的表示一个对象。
枚举
TS支持数字枚举和字符串枚举
enum Direction {
Up,
Down,
Left,
Right,
}
Up值为0,依次向后累加
// 字符串枚举没有类似数字枚举自增长的行为
复合类型
包含多个单一类型的类型。
数组类型
// [] 普通表示
let list: number[] = [1,2,3];
// 泛型表示
let list: Array<number> = [1,2,3];
元组类型
元组类型表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number];
x = ['hello', 10];
x = [10, 'hello'];
接口类型
interface Value {
value: string;
}
function printValue(value: Value) {
console.log(value.value);
}
高级类型
如果一个类型不能满足要求,我们就要考虑使用联合类型,交叉类型来解决问题。
联合类型
const sayHello = (name: string | undefined) => {...}
sayHello('bill');
sayHello(undefined);
interface foo {
name: string;
age: number;
}
interface bar {
name: string;
sex: string;
}
const func = (value: bar | foo): void => {
console.log(value.name)
}
只能输出value.name, 因为ts不知道你传入的是bar还是foo,只能打印相同的值
交叉类型
交叉类型是将多个类型合并为一个类型
interface foo {
name: string;
age: number;
}
interface bar {
name: string;
sex: string;
}
const func = (value: bar & foo): void => {
console.log(value.age)
}
因为使用交叉类型,ts将bar和foo合并为一个类型。
关键字
typeof
在TS中, typeof 操作符可以用来获取一个变量声明或对象的类型。
# 获取函数类型
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray;
keyof
keyof 操作符可以用来获取一个对象的所有key值:
interface Person {
name: string;
age: number;
}
type K1 = keyof Person;
type K2 = keyof Person[];
type K3 = keyof {[x: string]: Person};
in
in 用来遍历枚举类型:
type Keys = 'a' | 'b' | 'c';
type Obj = {
[p in keys]: any;
}
infer
在条件类型语句中吗,可以用infer 声明一个类型遍历并且对它进行使用。
# 获取函数的返回值的实现
type ReturnType<T> = T extends (...arg: any[]) => infer R ? R : any;
取到函数返回值的类型,方便之后使用。
extends
有时候我们定义的泛型类型不想过于灵活或者想继承某些类,可以通过extends关键字添加泛型约束。
interface hasLength {
length: number;
}
function print<T extends hasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
类型别名
类型别名用来给一个类型起一个新名字。 常用于联合类型
type Message = string | string[];
let greet = (message: Message) => {...}
类型断言
"尖括号" 语法
let str: any = 'this is a string';
let strLength: number = (<string>str).length;
as 语法
let str: any = 'this is a string';
let strLength: number = (str as string).length;
TS高级
类型保护
类型保护允许你使用更小范围下的对象类型。 TS熟知JS中 instanceof 和 typeof 运算符的用法,如果你在一个条件块中使用这些,TS将会推导出在条件块中的变量类型。
typeof
if (typeof x === 'string') {
// 在这个块中, TS知道x类型必须是 string;
console.log(x.subs(1)); // error: string中没有subs方法
console.log(x.substr(1)); //ok
}
TS 将会辨别string 上是否存在特定的函数,以及是否发生了拼写错误。
instanceof
class Foo {
foo = 123;
common = '123';
}
class Bar {
bar = 123;
common = '123';
}
function doStuff (arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.foo); // ok
console.log(arg.bar); //error
}
}
TS 甚至能够理解eles, 当使用if来缩小类型时,TS知道在其他块中的类型并不是if中的类型
function doStuff(arg: Foo | Bar) {
if (arg instanceof Foo) {
console.log(arg.foo); // ok
console.log(arg.bar); // error
} else {
console.log(arg.foo); // error
console.log(arg.bar); //ok
}
}
in
in操作符可以安全的检查一个对象上是否存在一个属性,它通常也被作为类型保护使用:
interface A {
x: number;
}
interface B {
y: string;
}
function doStuff(q: A | B) {
if ('x' in q) {
// q: A
} else {
// q: B
}
}
自定义类型保护的类型谓词
function isNumber(x: any): x is number {
return typeof x === "number";
}
function isString(x: any): x is string {
return typeof x === 'string';
}
函数
剩余参数
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
})
}
let a = [];
push(a, 1,2,3);
函数重载
函数重载或方法重载是使用相同名称和不同参数数量或类型创建多个方法的一种能力
就是为同一个函数提供多个函数类型定义进行函数重载,编译器会根据这个列表去处理函数的调用。
# 重载
function padding(all: number);
function padding(topAndBottom: number, leftAndRight: number);
function padding(top: number, right: number, bottom: number, left: number);
当TS编译器处理函数重载时,它会查找 重载列表,尝试使用第一个重载定义,如果匹配的话就使用这个。因此在定义重载的时候,一定要把最精确的定义放在前面。
接口
接口运行时的影响为0, 在TS接口中有很多方式及来声明变量的结构。
基础表示
interface Point {
x: number;
y: number;
}
可选属性
interface Point {
x?:number;
y?:number;
}
只读属性
interface Point {
readonly x: number;
readonly y: number;
}
定义函数
interface SearchFun {
(source: string, subString: string): boolean;
}
索引类型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray: ['bob', 'fred'];
let myStr: string = myArray[0];
接口继承
interface Shape {
color: string;
}
interface Square extends Shape {
slideLength: number;
}
let square = <Square>{};
square.color = 'blue';
square.sideLength = 10;
泛型
在像C#和java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据,这样用户就可以用自己的数据类型来使用组件。
设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是,类的实例成员,类的方法,函数参数和函数返回值。
泛型是允许同一个函数接受不同类型参数的一种模板,相比于使用any类型,使用泛型创建可复用的组件要更好,因为泛型会保留参数类型。
泛型函数
# 定义一个泛型变量
function indentity<T>(arg: T): T {
return arg;
}
# 定义多个泛型变量
function indentity<T, U>(value: T, message: U): T {
return value;
}
其中T代表Type, 在定义泛型时通常用作第一个类型变量名称。 但实际上T可以用任何有效名称代替, 除了T之外,以下是常见泛型变量代表的意思:
- K (key): 表示对象中的键类型;
- V (value): 表示对象中的值类型;
- E (Element): 表示元素类型;
泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x:T, y:T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
}
泛型约束
确保属性存在
function identity<T>(arg: T): T {
console.log(arg.length); // error;
return arg;
}
在这种情况下,编译器将不会知道T确实含有length属性,尤其是可以在将任何类型赋给类型变量T的情况下。 我们需要做的就是让类型变量extends一个含有我们所需属性的接口:
interface Length {
length: number;
}
function identity<T extends Length>(arg: T): T {
console.log(arg.length); // 可以获取length的属性
return arg;
}
检查对象上的键是否存在
泛型约束的另一个常见的使用场景就是检查对象上的键是否存在。
enum Difficulty {
Easy,
Intermediate,
Hard
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let tsInfo = {
name: "Typescript",
supersetOf": "Javascript1",
difficulty: Difficulty.Intermediate,
}
let difficulty: Difficulty = getProperty(tsInfo, 'difficulty'); //ok
let supersetOf: string = getProperty(tsInfo, 'superset_of'); //error
泛型工具检查
ReadOnly
将T中的类型都变为只读。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
- 通过keyof T 拿到T 的所有属性名。
- 使用in进行遍历,将值赋给P;
- 通过T[P] 取得相应的属性值;
- 对每个属性添加readonly 修饰符, 使其变为只读。
应用:
interface IPoint {
x: number;
y: number;
}
const start: Readonly<IPoint> {
x: 0;
y: 0;
}
Partial
将T中的类型都变为可选。
type Partial<T> = {
[P in keyof T]?: T[P];
}
- 通过keyof T 拿到 T 的所有属性名。
- 使用 in 进行遍历,将值赋给P;
- 通过 T[P] 取得相应的属性值;
- 对每个属性添加? 修饰符,使其变为可选。
应用:
interface Todo {
title: string;
description: string;
}
let newObj: Partial<Todo> {
title: "is title";
}
Record<K,T>
Record<K extends keyof any, T>的作用是将K中所有的属性的值转化为T类型。
type Record<K extends keyof any, T> = {
[P in K]: T;
}
应用
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
about: {title: 'about'};
}
Pick<T, K>
Pick <T, K extends keyof T>的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
应用
interface Todo {
title: string;
desciption: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">
const todo: TodoPreview = {
title: 'sss',
completed: false,
}
Exclude<T, U>
Exclude<T, U>的作用是将某个类型中属于另一个的类型移除掉。
type Exclude<T, U> = T extends U ? never : T;
如果T能赋值给U类型的话,那么就会返回never类型, 否则返回T类型。最终实现的效果就是将T中某些属于U的类型移除掉。
type T0 = Exclide<"a" | "b" | "c" , "a">
T0 = "b" | "c"
ReturnType
ReturnType的作用是用于获取函数T的返回类型。
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
应用
type T0 = ReturnType<() => string> // string;
type T1 = ReturnType<(s: string) => void> // void
type T2 = ReturnType<<T>() => T>: // {}
type T3 = RerturnType<<T extends U, U extends number[]> () => T>; // number[];