简介
TypeScript 其实就是类型化的 JavaScript,它不仅支持 JavaScript 的所有特性,还在 JavaScript 的基础上添加了静态类型注解扩展。
原始类型
在 JavaScript 中,原始类型指的是非对象且没有方法的数据类型,它包括 string、number、bigint、boolean、undefined 和 symbol 这六种 (null 是一个伪原始类型,它在 JavaScript 中实际上是一个对象,且所有的结构化类型都是通过 null 原型链派生而来)。
字符串、数字、布尔值、symbol
firstName: string = 'ahao';
familyName: string = String('zhang');
age: number = 30;
isMale: boolean = true;
symbolTest: symbol = Symbol('test');
personInfo: string = `My name is ${this.firstName} familyName is ${this.familyName}; age is ${this.age};`;
数组、元组
TypeScript 的数组和元组转译为 JavaScript 后都是数组, 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同
nameArray:string[]=['ahao','zhang'];
infoTuple:[string,string,number]=['ahao','zhang',30];
特殊类型
any
我们可以把任何类型的值赋值给 any 类型的变量,也可以把 any 类型的值赋值给任意类型(除 never 以外)的变量
anyTest:any
this.anyTest = this.age;
this.anyTest = this.firstName;
this.anyTest = this.nameArray;
unknown
与 any 不同的是,unknown 在类型上更安全。比如我们可以将任意类型的值赋值给 unknown,但 unknown 类型的值只能赋值给 unknown 或 any,
let result: unknown;
result = 3;
let testUnknown: any = result;
let testUnknown2: unknown = result;
// let testUnknown3:number=result;
console.log(testUnknown);
void、undefined、null、never
void 类型,它仅适用于表示没有返回值的函数
undefined 的最大价值主要体现在接口类型,它表示一个可缺省、未定义的属性
null 的价值我认为主要体现在接口制定上,它表明对象或属性可能是空值
never 表示永远不会发生值的类型;常用语报错函数及死循环
类型断言
- 使用 as 语法做类型断言
- 使用尖括号+类型的语法
<number>
const arrayNum: number[] = [1, 2, 3, 4];
// 使用 as 语法做类型断言 推荐使用as语法
const greeterThan2: number = arrayNum.find(num => num > 2) as number;
// 使用尖括号 + 类型的格式做类型断言
const greeterThan3: number = <number>arrayNum.find(num => num > 3);
非空断言
在值(变量、属性)的后边添加 '!' 断言操作符,它可以用来排除值为 null、undefined 的情况
interface接口类型
/ ** 关键字 接口名称 */
interface ProgramLanguage {
/** 语言名称 */
name: string;
/** 使用年限 */
age: () => number;
}
可缺省属性
在接口属性后边添加 ?来表示当前属性值可缺省;当属性被标注为可缺省后,它的类型就变成了显式指定的类型与 undefined 类型组成的联合类型
/** 关键字 接口名称 */
interface OptionalProgramLanguage {
/** 语言名称 */
name: string;
/** 使用年限 */
age?: () => number;
}
let OptionalTypeScript: OptionalProgramLanguage = {
name: 'TypeScript'
}; // ok
只读属性
通过 readonly标识该属性为只读属性
interface ReadOnlyProgramLanguage {
/** 语言名称 */
readonly name: string;
/** 使用年限 */
readonly age: (() => number) | undefined;
}
let ReadOnlyTypeScript: ReadOnlyProgramLanguage = {
name: 'TypeScript',
age: undefined
}
/** ts(2540)错误,name 只读 */
ReadOnlyTypeScript.name = 'JavaScript';
继承与实现
通过 extend关键字实现接口的继承与被继承
interface DynamicLanguage extends ProgramLanguage {
rank: number; // 定义新属性
}
interface TypeSafeLanguage extends ProgramLanguage {
typeChecker: string; // 定义新的属性
}
/** 继承多个 */
interface TypeScriptLanguage extends DynamicLanguage, TypeSafeLanguage {
name: 'TypeScript'; // 用原属性类型的兼容的类型(比如子集)重新定义属性
}
/** 类实现接口 */
{
class LanguageClass implements ProgramLanguage {
name: string = '';
age = () => new Date().getFullYear() - 2012
}
}
Type类型别名
type别名名字 = 类型定义”的格式来定义类型别名;类型别名能够实现组合类型和交叉类型
/** 联合 */
type MixedType = string | number;
/** 交叉 */
type IntersectionType = { id: number; name: string; } & { age: number; name: string };
/** 提取接口属性类型 */
type AgeType = ProgramLanguage['age'];
枚举
7 种常见的枚举类型:数字类型、字符串类型、异构类型、常量成员和计算(值)成员、枚举成员类型和联合枚举、常量枚举、外部枚举。
数字类型
仅仅指定常量命名的情况下,我们定义的就是一个默认从 0 开始递增的数字集合,称之为数字枚举
enum Day {
SUNDAY = 1,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}
字符串枚举
将定义值是字符串字面量的枚举称之为字符串枚举
enum Day {
SUNDAY = 'SUNDAY',
MONDAY = 'MONDAY',
...
}
异构枚举
TypeScript 支持枚举类型同时拥有数字和字符类型的成员,这样的枚举被称之为异构枚举
enum Day {
SUNDAY = 'SUNDAY',
MONDAY = 2,
...
}
常量成员和计算(值)成员
常量成员包含:
- 引用来自预先定义的常量成员,比如来自当前枚举或其他枚举;
- 圆括弧 () 包裹的常量枚举表达式;
- 在常量枚举表达式上应用的一元操作符 +、 -、~ ;
- 操作常量枚举表达式的二元操作符 +、-、*、/、%、<<、>>、>>>、&、|、^。
除常量成员外其他的都是计算值成员
enum FileAccess {
// 常量成员
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// 计算成员
G = "123".length,
}
常量枚举(const enums)
通过添加 const 修饰符定义常量枚举
const enum Day {
SUNDAY,
MONDAY
}
const work = (d: Day) => {
switch (d) {
case Day.SUNDAY:
return 'take a rest';
case Day.MONDAY:
return 'work hard';
}
}
}
外部枚举(Ambient enums)
通过 declare 描述一个在其他地方已经定义过的变量; 也可以使用 declare 描述一个在其他地方已经定义过的枚举类型,通过这种方式定义出来的枚举类型,被称之为外部枚举
declare let $: any;
$('#id').addClass('show'); // ok
泛型
泛型指的是类型参数化,即将原来某种具体的类型进行参数化。和定义函数参数一样,我们可以给泛型定义若干个类型参数,并在调用时给泛型传入明确的类型参数。设计泛型的目的在于有效约束类型成员之间的关系,比如函数参数和返回值、类或者接口成员和方法之间的关系
function reflect<P>(param: P) {
return param;
}
泛类型
在类的定义中,我们还可以使用泛型用来约束构造函数、属性、方法的类型
class Memory<S> {
store: S;
constructor(store: S) {
this.store = store;
}
set(store: S) {
this.store = store;
}
get() {
return this.store;
}
}
const numMemory = new Memory<number>(1); // <number> 可缺省
const getNumMemory = numMemory.get(); // 类型是 number
numMemory.set(2); // 只能写入 number 类型
const strMemory = new Memory(''); // 缺省 <string>
const getStrMemory = strMemory.get(); // 类型是 string
strMemory.set('string'); // 只能写入 string 类型
常用的官方工具类
操作接口类型
Partial
Partial 工具类型可以将一个类型的所有属性变为可选的,且该工具类型返回的类型是给定类型的所有子集
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface Person {
name: string;
age?: number;
weight?: number;
}
type PartialPerson = Partial<Person>;
// 相当于
interface PartialPerson {
name?: string;
age?: number;
weight?: number;
}
Required
Required 工具类型可以将给定类型的所有属性变为必填的,映射类型在键值的后面使用了一个 - 符号,- 与 ? 组合起来表示去除类型的可选属性,因此给定类型的所有属性都变为了必填。
type Required<T> = {
[P in keyof T]-?: T[P];
};
type RequiredPerson = Required<Person>;
// 相当于
interface RequiredPerson {
name: string;
age: number;
weight: number;
}
Readonly
Readonly 工具类型可以将给定类型的所有属性设为只读,这意味着给定类型的属性不可以被重新赋值
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type ReadonlyPerson = Readonly<Person>;
// 相当于
interface ReadonlyPerson {
readonly name: string;
readonly age?: number;
readonly weight?: number;
}
Pick
Pick 工具类型可以从给定的类型中选取出指定的键值,然后组成一个新的类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type NewPerson = Pick<Person, 'name' | 'age'>;
// 相当于
interface NewPerson {
name: string;
age?: number;
}
Omit
与 Pick 类型相反,Omit 工具类型的功能是返回去除指定的键值之后返回的新类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type NewPerson = Omit<Person, 'weight'>;
// 相当于
interface NewPerson {
name: string;
age?: number;
}
Exclude
Omit 类型的实现中,我们使用了 Exclude 类型。通过使用 Exclude 类型,我们从接口的所有属性中去除了指定属性,因此,Exclude 的作用就是从联合类型中去除指定的类型。
type Exclude<T, U> = T extends U ? never : T;
type T = Exclude<'a' | 'b' | 'c', 'a'>; // => 'b' | 'c'
type NewPerson = Omit<Person, 'weight'>;
// 相当于
type NewPerson = Pick<Person, Exclude<keyof Person, 'weight'>>;
// 其中
type ExcludeKeys = Exclude<keyof Person, 'weight'>; // => 'name' | 'age'
Extract
Extract 类型的作用与 Exclude 正好相反,Extract 主要用来从联合类型中提取指定的类型,类似于操作接口类型中的 Pick 类型。
type Intersect<T, U> = {
[K in Extract<keyof T, keyof U>]: T[K];
};
interface Person {
name: string;
age?: number;
weight?: number;
}
interface NewPerson {
name: string;
age?: number;
}
type T = Intersect<Person, NewPerson>;
// 相当于
type T = {
name: string;
age?: number;
};
Record
Record 的作用是生成接口类型,然后我们使用传入的泛型参数分别作为接口类型的属性和值。
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type MenuKey = 'home' | 'about' | 'more';
interface Menu {
label: string;
hidden?: boolean;
}
const menus: Record<MenuKey, Menu> = {
about: { label: '关于' },
home: { label: '主页' },
more: { label: '更多', hidden: true },
};
Record 类型接收了两个泛型参数:**第一个参数作为接口类型的属性,第二个参数作为接口类型的属性值。**keyof any 指代可以作为对象键的属性JavaScript 仅支持 string、number、symbol作为对象的键值。