TypeScript简介
TypeScript 是由微软开发的自由和开源的编程语言。通过在 JavaScript的基础上添加静态类型定义构建而成。TypeScript 通过 TypeScript 编译器或 Babel 转译为 JavaScript 代码
本质上运行的代码还是JavaScript,只是TypeScript提供了在编译时的静态类型检查,在开发阶段就可以发现问题,而不是在运行时才发现问题。
在TypeScript项目中,往往需要tsconfig.json文件来配置TypeScript的编译选项。
类型推断
在很多情况下,TypeScript 会根据上下文环境自动推断出变量的类型
let str = 'this is string'; // 等价 let str: string = 'this is string
TypeScript 这种基于赋值表达式推断类型的能力称之为类型推断
类型断言
类型检测无法做到绝对智能,毕竟程序不能像人一样思考。有时会碰到我们比 TypeScript 更清楚实际类型的情况。此时可以使用类型断言来指定一个值的类型。
const arrayNumber: number[] = [1, 2, 3, 4];
// const greaterThan2: number = arrayNumber.find(num => num > 2) :number || undefined
const greaterThan2: number = arrayNumber.find(num => num > 2) as number;( 使用`as` 断言为number类型)
const greaterThan2: number = <number>arrayNumber.find(num => num > 2) (使用尖括号语法)
非空断言
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。即排除了null和undefined类型。
let mayNull: null | undefined | number;
const test = mayNull!.toString(); // ok
const test2 = mayNull.toString(); // error
type NumGenerator = () => number;
function myFunc(numGenerator: NumGenerator | undefined) {
// Object is possibly 'undefined'.(2532)
// Cannot invoke an object which is possibly 'undefined'.(2722)
const num1 = numGenerator(); // Error
const num2 = numGenerator!(); //OK
}
确定赋值断言
实例属性和变量声明后面放置一个! 号,从而告诉 TypeScript 该属性会被明确地赋值。
let x: number;
initialize();
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error
function initialize() {
x = 10;
}
-------------------------------------------------------------
let x!: number;
initialize();
console.log(2 * x); // Ok
function initialize() {
x = 10;
}
字面量类型
TypeScript 支持 3 种字面量类型:字符串字面量类型、数字字面量类型、布尔字面量类型,对应的字符串字面量、数字字面量、布尔字面量分别拥有与其值一样的字面量类型
let specifiedStr: 'this is string' = 'this is string';
let specifiedNum: 1 = 1;
let specifiedBoolean: true = true
同时这里也涉及了一个需要注意的点。 'this is string' 虽然是一个string类型,但不是所有string都是'this is string'
'this is string'可以赋值给其他string类型不报错,但string赋值给'this is string'就不一定了。
字面量类型的应用场景
字面量类型往往与联合类型一起使用
type Direction = 'up' | 'down';
function move(dir: Direction) {
// ...
}
move('up'); // ok
move('right'); // ts(2345) Argument of type '"right"' is not assignable to parameter of type 'Direction'
---------------------------------------------------------------------------------------
interface Config {
size: 'small' | 'big';
isEnable: true | false;
margin: 0 | 2 | 4;
}
const config: Config = {
size:'small',
isEnable: true,
margin: 0
}
const、let 对类型推断的作用
一般情况下,使用const声明的变量,TypeScript直接 推断赋值字面量的类型。let会推断赋值字面量类型的父类型。
const str = 'this is string'; // str: 'this is string'
const num = 1; // num: 1
const bool = true; // bool: true
-------------------------------------------------------------
let str = 'this is string'; // str: string
let num = 1; // num: number
let bool = true; // bool: boolean
联合类型
联合类型表示取值可以为多种类型中的一种,使用 | 分隔每个类型。
type Direction = 'up' | 'down' | 'left' | 'right';
function move(dir: Direction) {
// ...
}
move('up'); // ok
move('right'); // ok
move('top'); // ts(2345) Argument of type '"top"' is not assignable to parameter of type 'Direction'
类型别名
类型别名可以给一个类型起一个新的名字,方便使用。
type Name = string;
type Age = number;
type Person = {
name: Name;
age: Age;
};
const person: Person = {
name: 'Tom',
age: 25
};
交叉类型
交叉类型是将多个类型合并为一个类型。它包含了所需的所有类型的特性,使用&定义。
以下是一个有问题的例子
type Useless = string & number;
因为没有任何属性既是 string 类型又是 number 类型。因此,在上述的代码中,类型别名 Useless 的类型就是个 never
type IntersectionType = { id: number; name: string; } & { age: number };
const mixed: IntersectionType = {
id: 1,
name: 'name',
age: 18
}
交叉用于多个接口类型合并成一个类型,等同接口继承的效果,也就是所谓的合并接口类型
值得注意多个接口类型存在同名属性时,会对同名属性进行交叉合并,而不是覆盖
接口
接口定义了对象的形状,可以用来定义函数、类、对象等。接口中的属性必须是某种类型,属性的名字和类型是必需的。
interface Person {
name: string;
age: number;
sayHello(): void;
}
const person: Person = {
name: 'Tom',
age: 25,
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
};
只读类型
在接口的属性名前面加上 readonly 关键字,可以将属性设置为只读。
interface Person {
readonly name: string;
age: number;
sayHello(): void;
}
const person: Person = {
name: 'Tom',
age: 25,
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
};
person.name = 'Jerry'; // error
可选属性
在接口的属性名前面加上 ? 号,可以将属性设置为可选。
interface Person {
name?: string;
age: number;
sayHello(): void;
}
const person: Person = {
age: 25,
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
};
索引签名
索引签名可以为数组、元组、对象类型添加额外的约束。
interface StringArray {
[index: number]: string;
}
const stringArray: StringArray = ['hello', 'world'];
stringArray[0] = 'hi'; // ok
stringArray[1] = 123; // error
函数类型
函数类型可以用来定义函数的形状。
interface AddFunc {
(x: number, y: number): number;
}
const add: AddFunc = (x, y) => x + y;
一旦定义了任意属性,那么确定属性和可选属性 的类型都必须是它的类型的子集
interface 和 type 的区别
interface 和 type 都可以用来定义对象类型,但是两者有一些区别。
interface Person {
name: string;
age: number;
sayHello(): void;
}
type PersonType = {
name: string;
age: number;
sayHello(): void;
}
两者的区别主要体现在以下方面:
-
interface用来定义对象的形状,可以包含属性、方法、索引签名等。 -
type用来定义类型别名,可以用来给一个类型起一个新的名字。 -
interface用来定义对象的形状,可以包含属性、方法、索引签名等。 -
type用来定义类型别名,可以用来给一个类型起一个新的名字。
这里简单的说明一下作者的理解和用法:
interface用来定义一个对象的数据结构类型type用来指明该类型与其他类型的关系 (譬如联合类型...泛型)
详情参照其他文档interface vs type