TypeScript基础——小白入坑指南
一、编译
--outDir 指定编译文件输出目录
tsc --outDir 相较于当前命令行位置的指定文件夹的目录 要编译的ts文件地址
🌰:
// 将src下的index.ts编译到dist目录下
tsc --outDir ./dist ./src/index.ts
复制代码
--target 指定编译的代码版本目标,默认为ES3
// 将src下的index.ts编译为ES6版本的JS,并存放到dist目录下
tsc --outDir ./dist --target ES6 ./src/index.ts
复制代码
--watch 监听模式下运行,当TS文件发生改变的时候自动编译
// 将src下的index.ts编译为ES6版本的JS,并存放到dist目录下
tsc --outDir ./dist --target ES6 --watch ./src/index.ts
复制代码
编译配置文件 tsconfig.json
{
// 编译配置选项
"compilerOptions": {
// 指定输出目录,相对于当前tsconfig.json文件路径
"outDir": "./dist",
// 指定编译版本
"target": "es6",
// 监听模式
"watch": true
},
// include 指定当前编译要查找的目录和文件,相对于当前tsconfig.json
// ** : 所有目录(包括子目录)
// * : 所有文件,也可以指定类型
"include": [
"./src/**/*.ts"
]
}
// 命令行通过 tsc 命令执行 tsconfig.json
复制代码
指定加载的编译配置文件
使用 --project
或 -p
指定配置文件目录,会默认加载该目录下的 tsconfig.json
文件,也可指定某个具体的配置文件
二、类型
动态类型语言
定义
程序运行期间才做数据类型检查的语言,如:JavaScript
优点
静态类型语言的缺点
缺点
静态类型语言的优点
静态类型语言
定义
程序编译期间做数据类型检查的语言,如:Java
优点
程序编译阶段(配合IDE、编译器甚至可以在编码阶段)即可发现一些潜在错误,避免程序在生产环境运行了以后再出现错误
编码规范、有利于团队开发协作、也更有利于大型项目开发、项目重构
配合IDE、编辑器提供更强大的代码智能提示和检查
代码即文档
缺点
麻烦
缺少灵活性
三、类型系统
两个重要组成部分
类型标注(定义、注解) - typing
类型检测(检查) - type-checking
类型检测
顾名思义,就是对数据的类型进行检测。注意这里,重点是类型两字。
类型系统检测的是类型,不是具体值(虽然,某些时候也可以检测值),比如某个参数的取值范围 (1-100之间),我们不能依靠类型系统来完成这个检测,它应该是我们的业务层具体逻辑,类型系统检测的是它的值类型是否为数字!
类型标注
类型标注就是在代码中给数据 (变量、函数(参数、返回值))添加类型说明,当一个变量或者函数(参数)等被标注以后就不能存储或传入与标注类型不符合的类型。
有了标注, Typescript 编译器就能按照标注对这些数据进行类型合法检测。
有了标注,各种编辑器、IDE等就能进行智能提示。
数据载体: 类型
let name: string = 'Jerry'
复制代码
基础的简单的类型标注
1. 基础类型 - 基础类型
let name: string = 'Jerry'
let isOk: boolean = true
let num: number = 1
复制代码
2. 空和未定义类型 - 基础类型
因为在null
和undefined
这两种类型有且只有一个值,在标注一个变量为null
和undefined
类型,那就表示改变量不能修改了
let a: null
let b: undefined
复制代码
默认情况下null
和undefined
是所有类型的子类型,也就是说可以把其他类型的变量设置为null
和undefined
let num: number = 1
num = null
// 存在的问题,TS无法检测但是运行起来会报错
num.toFixed(1)
// 解决方法:指定 strictNullChecks 配置为 true
复制代码
如果一个变量声明了但是未赋值,那么该变量的值为undefined
,但是如果它同时也没有标注类型的话,默认类型为any
// 类型为 string,值为undefined
let name: string
// 类型为 any,值为 undefined
let num
复制代码
获取的元素可能为null
let el = document.querySelect('div')
// 添加判断
if (el) {
el.style.display = 'none'
}
复制代码
3. 对象类型 - 非基础类型
// A、JS内置对象类型,如Object、Array、Date...,可以通过对象的【构造函数】或者【类】来标注
let data: Object = {}
let arr: Array<nubmer> = [1, 2, 3]
let d: Date = new Date()
// B、自定义对象类型
// 1. 字面量标注
let user: {name: string, age: number} = {
name: 'Jerry',
age: 20
}
user.sex = '男' // Error
// 2. 接口interface
interface Person {
name: string
number: number
}
let user: Person = {
name: 'Jerry',
age: 20
}
// interface缺点:接口只能作为类型标注使用,不能作为具体值,
// 它只是一种抽象的结构定义,并不是实体,没有具体功能实现
// 3. 定义【类】或者【构造函数】
class Person {
constructor (public user: string, public age: number) {}
}
let user: Person = new Person('Jerry', 35)
// C、包装对象
let a: string // => 字符串
a = '1' // Right
a = new String('1') // Error
let a: String// => 字符串对象
a = '1' // Right
a = new String('1') // Right
// 字符串对象有的字符串不一定有(对象有的,基础类型不一定有)
// 字符串有的字符串对象一定有
复制代码
4. 数组类型 - 非基础类型
TypeScript
中数组存储的类型必须一致,所以在标注数组类型的时候,同时要标注数组中存储的数据类型
A、使用泛型标注
let arr = Array<number> = []
arr.push(100) // Right
arr.push('Jerry') // Error
B、简单标注
let arr2: string[] = []
arr2.push('Jerry') // Right
复制代码
5. 元组类型 - 特殊类型
元组类似数组,但是存储的元素类型不必相同,但是需要注意:
初始化数据的个数以及对应位置标注类型必须一致
越界(新增)数据必须是元组标注中的类型之一(标注越界数据可以不用对应顺序 - 联合类型)
// 声明字符串、数组类型,那么初始化数据也必须是先字符串后数字类型且必须有这两个类型对应的数据
let data: [string, number] = ['Jerry', 20]
data.push('Tom') // Right
data.push(30) // Right
data.push(true) // Error
复制代码
6. 枚举类型- 特殊类型
组织收集一组关联数据的方式,通过枚举我们可以给一组有关联意义的数据赋予一些友好的名字
key只能是字符串,不能是数字
value只能为数字和字符串
value若是数字,称为数字类型枚举;
value若是字符串,称为字符串类型枚举;
value默认值为数字:0
枚举值可以省略,如果省略,则:
若第一个枚举值默认为1,则非第一个枚举值为上一个数字枚举值+1
如果上一个枚举值为字符串,则后续枚举值必须手动赋值
枚举值为只读(常量),初始化后不允许修改
// 枚举名称一般全大写
enum HTTP_CODE {
DEFAULT, // 默认为0
OK = 200,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED // 此处默认为405
}
// 定义完成后无法赋值
HTTP_CODE.OK = 1 // Error
// 取值
HTTP_CODE.OK // 200
HTTP_CODE['200'] // 'OK'
复制代码
7. 无值类型- 特殊类型
表示没有任何数据的类型,通常用于标注无返回值函数的返回值类型,函数默认标注类型为:void
function fn (): void {
// 没有return 或者 return undefined
}
// *tsconfig.json 配置 strictNullChecks 为 false,则允许 return null
复制代码
8. Never类型- 特殊类型
当一个函数永远不可能执行return
的时候,返回的就是never
,与void
不同,void
是执行了return
,只是没有值,never
是不会执行return
,比如抛出错误,导致函数终止执行
function fn(): never {
throw new Error('error')
}
复制代码
9. 任意类型- 特殊类型
有的时候,我们并不确定这个值到底是什么类型或者不需要对该值进行类型检测,就可以标注为any
类型
let a: any
a = 1 // Right
let b: number
b = a // Right
复制代码
一个变量声明未赋值且未标注类型的情况下,默认为
any
类型任何类型都可以赋值给
any
类型any
类型也可以赋值给任何类型any
类型有任意属性和方法
*tsconfig.json
配置noImplicitAny
配置为true
,会禁止出现any
类型
10. 未知类型 unknow(Version 3.0 Added)- 特殊类型
3.0版本中新增,属于安全的any
,与any
区别:
unknow
仅能赋值给unknow
、any
unknow
没有任何属性和方法
let a: any = 'Jerry'
let b: number = 1
b.toFixed(2) // Right
b = a
b.toFixed(2) // 编辑器不会报错,但是实际是错误的
let a: unknow = 'Jerry'
let b: number = 1
d = a // Error,unknow仅能赋值给unknow、any
复制代码
11. 函数类型
在JavaScript中函数是非常重要的,在TypeScript也是如此。同样是,函数也有自己的类型格式标注
参数
返回值
函数名称 (参数1: 参数1类型, 参数2: 参数2类型): 返回值类型 {}
function add (x: number, y: number): number {
return x + y
}
复制代码
四、接口interface
对复杂的数据类型(对象)进行标注
interface Person {
name: string
age: number
}
let user: Person = {
name: 'Jerry',
age: 20
}
// 注意:接口是一种类型,不能作为值使用
let user = Person // Error
复制代码
属性
interface Person {
name: string
age: number
// 1、可选属性,可传可不传
sex?: string
// 2、只读属性,初始化的时候赋值,后期无法修改
readonly hobby: string
// 3、任意属性,索引只能为【字符串】或【数字】
[key: string]: string // 允许扩展一个字符串类型的属性
[key: number]: number // 允许扩展一个数字类型的属性
}
let user: Person = {
name: 'Jerry',
age: 20,
hobby: '篮球'
}
user.education = '本科' // 任意属性
复制代码
🔥注意:当同时存在数字类型索引和字符串类型索引的时候,数字类型的值类型必须是字符串的值类型或子类型
interface Person1 {
[key: string]: string
[key: number]: number // Error
// number 不是 string 的子类型
}
interface Person2 {
[key: string]: Object
[key: number]: Date // Right
// Date 是 Objet 的子类型
}
复制代码
使用接口描述函数
interface 接口名称 {
(参数1: 参数1类型, 参数2: 参数2类型): 返回值类型
}
interface User {
(name: string, age: number): string
// 注意不要加key,即fn,这样不是定义一个函数,而是定义一个对象,对象下有一个fn的方法
fn (name: string, age: number): string
}
let userFn: User = function (name, age) {
return `${name}的年龄是${age}岁`
}
// 使用场景,定义回调函数
interface CallBack {
(a: nummbr, b: number): number
}
function todo (callback: CallBack) {
const result = callback(1, 2)
}
todo(function (a: number, b: number): number {
return a + b
})
复制代码
接口合并
多个同名的接口合并成一个接口
interface Person {
name: string
}
interface Person {
age: number
// 定一个对象下的名为 fn 的方法
fn (a: string): string
}
interface Person {
age: number // Right
age: string // Error
// 定一个对象下的名为 fn 的方法
fn (a: number): number // Right
}
let user: Person = {
name: 'Jerry'
age: 20,
fn: function (a: any): any {}
}
复制代码
如果合并的接口存在同名的非函数成员,则必须保证他们类型一致,否则编译报错
接口中的同名函数则是采用重载
五、高级类型
联合类型
联合类型也可以称为多选类型,当我们希望标注一个变量为多个类型之一时可以选择联合类型标注,【或】的关系
function css (ele: Element, attr: string, value: string | number) {}
css(el, 'width', '100px') // Right
css(el, 'opacity', 0) // Right
css(el, 'opacity', [1, 2]) // Error
复制代码
交叉类型
交叉类型也可以称为合并类型,可以把多种类型合并到一起成为一种新的类型,【并且】的关系
interface Type1 {
x: number
y: string
}
interface Type2 {
z: number
}
const data: Type1 & Type2 = {
x: 1,
y: 'Jerry',
z: 2
}
复制代码
字面量类型
有的时候我们希望标注的不是某个类型,而是一个固定值,就可以使用字面量类型,常配合联合类型(或)使用。
function user (name: string, sex: '男' | '女') {}
复制代码
类型别名
有的时候类型标注比较复杂,这个时候我们可以使用type
关键字给类型标注起一个相对简单的名字
// 字面量类型别名
type Sex = '男' | '女' | '未知'
// 联合类型别名
type Age = string | number
function user (name: string, sex: Sex, age: Age) {}
// 函数类型别名,与接口interface有点区别
type callback = (a: string) => string
let fn: callback = function (a) {}
// 或者直接
let fn: (a: string) => string => function (a) {}
复制代码
interface
与type
的区别
描述区别:
-
interface
只能描述object/class/function
的类型;type
能描述所有数据
名称区别:
-
同名
interface
自动合并,利于扩展;type
不能重名
类型推导
每次都显式标注类型会比较麻烦,TypeScript提供了一种更加方便的特性:类型推导。
TypeScript编译器会根据上下文自动的推导出对应的类型标注,这个过程发生在:
初始化变量
设置函数默认参数值
返回函数值
// 自动推断 x 为 number
let x = 1
// 不能将类型 'a' 分配给类型 number
x = 'a'
// 函数参数类型、函数返回值会根据对应的默认值和返回值进行自动推断
// 根据参数默认值推断 a 为 number
function fn (a = 1) {
// 推断返回值为 number
return a * a
}
复制代码
类型断言
有的时候,我们可能标注一个更加精确的类型来缩小类型标注范围,比如:
let img = document.querySelector('#img')
img.src = 'xxx' // Error => 类型"Element"上不存在属性"src"
复制代码
我可以看到img
的类型为Element
,而Element
类型其实只是元素类型的通用类型,如果我们去访问src
这个属性是有问题的,我们需要把它的类型标注得更加精确:HTMLImageElement
类型,这个时候,我们就可以使用类型断言,它类似于一种类型转换:
let img = <HTMLImageElement>document.querySelector('#img')
// 或者
let img = document.querySelector('#img') as HTMLImageElement
img.src = 'xxx' // Right
复制代码
注意:断言只是一种预判,并不会对数据本身产生实际的作用,即:类似转换,但并非真的转换了
大厂面试题分享 面试题库
前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
面试小助手