Typescript
TS是JS的超集, TS在JS的基础上加入了类型系统,让参数都有明确的类型,从而带来了更智能的提示。
安装TS
npm install -g typescript
查看版本
tsc -v
编译文件
tsc hello.ts
# hello.ts => hello.js
项目安装typescipt
npm i -D typescript ts-loader
生成tsconfig.json配置文件
npx tsc --init
在线编译
类型注解
- 作用于变量,方法的形参和返回值
TS的基本数据类型
TS的基本数据类型分类
- 基础类型
- string
- number
- boolean
- undefined
- null
- symbol
- bigint
- 引用类型
- object
- array
- tuple
- function
- 特殊类型
- any
- unknown
- nerver
- void
- enum
- 其他类型
- 类型推理
- 字面量类型
- 交叉类型
1. 基础类型
string
let name: string = 'aaa'
number
let num: number = 12
boolean
let status: boolean = true
undefined
- null 和 undefined 是所有类型的子类型
- null 和 undefined 两个类型一旦赋值上,就不能在赋值给任何其他类型
let un: undefined = undefined
let name: string // 不报错
let name: string = undefined // 不报错
let name: string = null // 不报错
null
let nu: null = null
symbol
- symbol是独一无二的,即使声明两个值为一样的symbol, 判断也不相等
let sym: symbol = Symbol();
bigint
let big: bigint = 10n
2. 引用类型
Array
let arr: number[] = [1, 2, 3]
let arr: Array<number> = [1, 2, 3]
let arr: Array<number | string> = ['a', 1, 2] // 多个类型
object
- object 非原始类型,在定义上直接使用 object 是可以的
let obj: object = { a: 1, b: 2}
obj.a = 3 // error
let obj2: { a: number, b: number } = {a: 1, b: 2}
obj2.a = 3 // ok
- Object(大写的O), 代表所有的原始类型或非原始类型都可以进行赋值,除了null和undefined
let obj: Object;
obj = 1; // ok
obj = "a"; // ok
obj = true; // ok
obj = {}; // ok
obj = Symbol() //ok
obj = 10n //ok
obj = null; // error
obj = undefined; // error
tuple
let temp: [number, string] = [1, '2'] // ok
let temp1: [number, string] = [1, 3] // error
let temp2: [number, string] = [1] // error
let temp3: [number, string] = [1, '1', true] // error
function
// function
function add(x: number, y: number): number {
return x + y;
}
// 箭头函数
let add = (x: number, y: number): number => {
return x + y;
}
参数类型
- 可选参数: 如果函数要配置可有可无的参数时,可以通过 ? 实现,可选参数一定要在最后面
- 默认参数:函数内可以自己设定其默认参数,用 = 实现
- 剩余参数:仍可以使用扩展运算符 …
// 可选参数
const setInfo = (name: string, age?: number) => {
console.log(name, age)
}
setInfo('Domesy') //"Domesy", undefined
setInfo('Domesy', 7) //"Domesy", 7
// 默认参数
const setInfo = (name: string, age: number = 11) => {
console.log(name, age)
}
setInfo('Domesy') //"Domesy", 11
setInfo('Domesy', 7) //"Domesy", 7
// 剩余参数
const allCount = (...numbers: number[]) => {
let sum = numbers.reduce((val, item) => (val += item), 0)
console.log(`数字总和为:${sum}`)
}
allCount(1, 2, 3) //"数字总和为:6"
函数重载
function setInfo(val: string): void {
console.log(val)
}
function setInfo(val: number): void{
console.log(val)
}
setInfo("Domesy")
setInfo(7)
3. 特殊类型
any
- 在 TS 中,任何类型都可以归于 any 类型, 顶级类型
- 如果不指定变量的类型,则默认为any类型
- 不推荐使用该类型, 丧失了TS的作用
let val:any // 等价于 let val
val = '1'
val = 2
val = true
val = [1, 2, 3]
val = {}
unknown
- unknow只能赋值给unknow类型和any类型,顶级类型
let val:unknown // 等价于 let val
val = '1'
val = 2
val = true
val = [1, 2, 3]
val = {}
let val1:any // 等价于 let val
val1 = '1'
let value1:unknown = val //ok
let value2:unknown = val1 //ok
let value4:string = val //error
void
- 当一个函数,没有返回值时,TS会默认他的返回值为 void 类型
const setInfo = ():void => {}
// 等价于
const setInfo = () => {}
const setInfo1 = ():void => { return '1' } // error
never
- 表示一个函数永远不存在返回值, never应该是 void子集
- 符合never的情况有:当抛出异常的情况和无限死循环
let error = ():never => { // 等价约 let error = () => {}
throw new Error("error");
};
let error1 = ():never => {
while(true){}
}
枚举
const Status = {
ONE: 1,
TWO: 2,
THREE: 3
}
// 默认从零开始
enum Status {
ONE, // 0
TWO, // 1
THREE, // 2
}
// 指定开始项的值
enum Status {
ONE: 1, // 1
TWO, // 2
THREE, // 3
}
// 指定所有项的值
enum Status {
ONE: 1,
TWO: 2,
FOUR: 4,
NAME: 'name'
}
function getName(name: number): number {
if (name === Status.ONE) return Status.ONE
else if (name === Status.TWO) return Status.TWO
else return Status.THREE
}
其他类型
类型推论
- 如果没有明确指定类型,ts按照类型推论的规则推断出一个类型
let name // 推断为any
let name = 'rmy' // 推断为 string
let num = 13; // 推断为 number
字面量类型
- 在TS中,我们可以指定参数的类型是什么
- 目前支持字符串、数字、布尔三种类型
let str:'aaa' // aaa 类型
let num: 1 | 2 | 3 = 1 // 1,2,3 类型
str = 'aaa' // ok
num = 2 // ok
num = 7 // error
联合类型
- 同时指定多个类型,从左往右的或判断
let nameMutilType: (string | number)
交叉类型
- 将多个类型合并为一个类型,使用&符号连接
type AProps = { a: string }
type BProps = { b: number }
type allProps = AProps & BProps
const Info: allProps = {
a: 'aaa',
b: 7
}
interface AProps { a: string }
interface BProps { b: number }
type allProps = AProps & BProps
const Info: allProps = {
a: 'aaa',
b: 7
}
接口 和 类型别名
接口 interface
- 使用接口来描述一个对象,不能描述基础类型
- 可以继承
- 可以自动合并声明
// 接口是对一个对象
interface LoginForm {
username: string,
password: string,
phone: (number | string), // 多个类型
email?: string, // 不确定项
[propname:string]: any, // 扩展不确定字段
goto(): string, // 方法
}
let loginForm: LoginForm = {
username: '',
password: '',
phone: '',
}
interface Person {
name: string,
age: number
}
function say(person: Person) {
return `hello, ${person.name} ${person.age}`
}
// 用于数组
let arr: Person[] = [
{ name: 'rmy', age: 25 }
]
// 继承
interface Student extends Person {
grade: string,
}
let stu: Student = {
name: 'rmy',
age: 18,
grade: '高三'
}
// 合并声明
interface User {
name: string
age: number
}
interface User {
sex: string
}
/* 自动合并结果为:
interface User {
name: string
age: number
sex: string
}
*/
类型别名 type
- 类型别名可以描述基本类型,也可以是对象
- 可以继承
- 可以联合类型
// 基础类型
type NameType = string
let name: NameType = 'aaa'
// 对象
type Person = {
name: string,
age: number,
}
let user: Person = {
name: 'rmy',
user: 25,
}
// 继承 &
type User = Person & {
grade: string
}
interface Name {
name: string
}
type User = Name & {
age: number
}
// 联合类型
interface Dog {
wong()
}
interface Cat {
miao()
}
type Pet = Dog | Cat
泛型
- 定义函数/接口/类时不预先指定类型,在使用时再指定具体的类型
// 定义有泛型的函数,T 是接收调用的类型
function identity<T>(arg: T): T {
return arg
}
// 使用泛型的函数,使用时指定T的类型
let output = identity<string>("myString")
function add<T>(a: T, b: T): T {
return a + b
}
add<number>(1, 2)
// 多个泛型
function add<T, P>(a: T, b: P) {
return `${a}${b}`
}
add<number, string>(1, 'a') // 使用时指定多个泛型
function getPersonAge<T>(person: T): number {
return person.age
}
getPersonAge<Person>({name: 'rmy', age: 25})
类
interface IPerson {
name: string,
age: number,
}
class Person {
status: boolean = false
constructor(person: IPerson) {
this.name = person.name
this.age = person.age
}
say(content: string) {
let text: string = `hello,${content}`
console.log(content)
}
}
class Student extends Person {
constructor() {
super.say('you are welcome') // super 指向父级
}
}
TS类型断言和类型守卫
TS断言
分为三种:类型断言、非空断言、确定赋值断言
当断言失效后,可能使用到:双重断言
1. 类型断言
- 类型断言好比其它语言里的类型转换
- 两种方式:1. 尖括号 2. as
- 尖括号语法在React中会报错,原因是与JSX语法会产生冲突,所以只能使用as语法
let someValue: number = 1
// 方式一
someValue = (<string>someValue)
// 方式二
someValue = (someValue as string)
2. 非空断言
- 在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。
const info = (name: string | null | undefined) => {
const str1: string = name // error name可能是null或者undefined
const str2: string = name! // ok
}
3. 确定赋值断言
- 允许在实例属性和变量声明后面放置一个 ! 号, 以告诉TS该属性会被明确赋值
let num: number;
let num1!: number;
const setNumber = () => num = 7
const setNumber1 = () => num1 = 7
setNumber()
setNumber1()
console.log(num) // error
console.log(num1) // ok
4. 双重断言
- 断言失效后,可能会用到,但一般情况下不会使用
- 失效的情况:基础类型不能断言为接口
interface Info{
name: string;
age: number;
}
const name = 'name' as Info; // error, 原因是不能把 string 类型断言为 一个接口
const name1 = 'name' as any as Info; //ok
类型守卫
in关键字
- 用于判断这个属性是那个里面的
interface Info {
name: string
age: number
}
let data: Info = {
name: 'aaa',
age: 25
}
if ('name' in data) {
console.log(`我的名字是:${data.name},年龄是:${data.age}`)
}
typeof关键字
- 用于判断基本类型,如string, number, boolean, null, undefined等
instanceof关键字
- 用于判断一个实例是不是构造函数或类的时候
类型谓词(is)
- is 全等
命名空间 与 模块系统
- “内部模块” 简称 命名空间
- “外部模块” 简称 模块
- 在不同的ts文件里命名同样的变量或方法是会报错的, ts所处的空间是全局的, 使用模块系统可以规避这个问题
1. 命名空间
- 使用关键字 namespace 定义
- 提供逻辑分组,避免命名冲突
- 一个文件里面定义多个命名空间,各个空间可以包含相同的属性和方法
-
- 使用模块时就没必要使用命名空间, 因为有模块系统的存在,所以我们平时开发基本上用不到命名空间
// namespace.ts
export namespace SomeNameSpace1 {
export const name = "rmy";
export const say = () => {
console.log("SomeNameSpace1");
};
}
export namespace SomeNameSpace2 {
export const name = "rmy";
export const say = () => {
console.log("SomeNameSpace2");
};
}
// 使用
import {SomeNameSpace1, SomeNameSpace2 } from 'namespace.ts'
let name = SomeNameSpace1.name
SomeNameSpace1.say()
2. 模块系统
- 模块系统是一个逻辑分组
- TypeScript 与 ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块
- 模块语法和ES Module中的import和export用法是一样的
// export.ts
export const a = 1
export type Person = {
name: String
}
export { a, Person }
export default (a = 1)
export default () => 'function'
import { a, Person } from './export';
import * as P from './export';
声明文件
- 以 .d.ts 结尾的文件就是声明文件
- 为 JS 代码提供类型声明
声明文件的位置
- 置到 tsconfig.json 文件中的 include 配置目录中
- 于 js 代码所在目录相同, 并且文件名也相同的文件
- 放置到 ```node_modules/@types 文件夹中
- 手动配置 tsconfig.json 中的 compilerOptions.typeRoots 选项, 配置申明文件的目录
如何编写声明文件
- 自动生成
- 项目是使用 ts 编写的, 在发布(编译)之后是 js 文件
- 在 tsconfig.json 文件中配置 compilerOptions.declaration : true,编译ts文件就会自动生成声明文件