Appearance
基础
01. 介绍
特点:
- 编译型语言:编译为 js 后运行,单独无法运行;
- 强类型语言;
- 面向对象的语言;
优势:
- 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用;
- 可以在编译阶段就发现大部分错误,这总比在运行时候出错好;
- 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等;
总结:TypeScript 增加了代码的可读性和可维护性。
1.1 安装
需要有 node 环境,通过npm
或者yarn
安装
bash
npm install -g typescript
1.2 编码
使用 .ts
文件扩展名, 使用 typescript
编写使用 React 时,使用 .tsx
扩展名 。
使用 :
指定变量的类型,:
的前后有没有空格都可以;
typescript
let str: string = 'Hello TypeScript!'
function add(num1: number, num2: number) {
return num1 + num2
}
console.log(str)
console.log(add(1, 2))
1.3 编译
使用 tsc 命令可编译 .ts 文件, 生成一个同名 .js 文件;编译的时候即使报错了,还是 会生成编译结果(.js),可通过 tsconfig.json
文件配置
bash
tsc index.ts
1.4 配置 tsconfig.json
json
{
"compilerOptions": {
"target": "ES6", // 用来指定 ES 版本
"module": "ESNext", // 指定要使用模块化的规范
"outDir": "./js", // // 用来指定编译后文件的存放位置
"strict": true // 开启严格模式
}
}
2. 基础类型
2.1 Number 类型
typescript
//Numbers类型声明
let n1: number = 100
let n2: number = 0b100
let n3: number = 0o100
let n4: number = 0x100
let n5: number = 100.11
console.log(n1, n2, n3, n4, n5) // Output: 100 4 64 256 100.11
2.2 Boolean 类型
typescript
// Boolean类型声明
let b1: boolean = true
let b2: boolean = 20 > 5
let b3: boolean = !!100
console.log(b1, b2, b3) //Output: true true true
2.3 String 类型
typescript
//string类型声明
let s1: string = 'bddxg'
let s2: string = `http://www.${s1}.top`
console.log(s1, s2) // Output: bddxg http://www.bddxg.top
2.4 Array 类型
typescript
//Array类型声明
let arr1: string[] = ['a', 'b', 'c', 'd', 'e']
let arr2: number[] = [1, 2.1, 0o100, 0x100, 3.33]
let arr3: Array<number> = [1, 2, 3] // 泛型
console.log(arr1, arr2, arr3) // Output: [ 'a', 'b', 'c', 'd', 'e' ] [ 1, 2.1, 64, 256, 3.33 ] [ 1, 2, 3 ]
2.5 特殊类型
typescript
//特殊类型
let t1: any // 表示任意类型 想当于关闭了ts类型检测
t1 = 'hello'
t1 = 123
t1 = 20 < 30
let t2: unknown = '大西瓜' // 未知类型,一个类型安全的any
let t22: string
// 第一种:if判断
if (typeof t2 === 'string') {
t22 = t2
console.log(t22)
}
// 第二种:断言
t22 = t2 as string
console.log(t22)
// 第三种:<>
t22 = <string>t1 // 当t1的值类型为string时才给t22赋值
//void用来表示空,以函数为例,表示没有返回值的函数
function v1(): void {
//return 123 //此时再return就会报错
}
2.6 对象和函数类型声明
typescript
let o = {
name: '小明',
age: 18,
}
console.log(o.name)
// o.gender = 'male'; // 无法添加属性
let userInfo: { name: string; age?: number } // age属性可以存在也可以没有
userInfo = { name: '小红' }
console.log(userInfo)
let school: { name: string; addr: string; [propName: string]: any } // [propName: string]:any 允许添加新的属性
school = { name: '小学', addr: '西街', students: 6000 }
console.log(school)
// 函数类型
let nums: number[] = [1, 3, 2, NaN, 10, 5, 4]
nums.sort((a, b) => a - b)
console.log(nums)
2.7 运算符号和应用
typescript
// 联合选择
let a: string | string[]
a = 'hello'
a = ['h', 'e', 'l', 'l', 'o']
// 同时(基本用不到)
let c: { name: string } & { age: number }
c = { name: '混混', age: 28 }
// 字面量 直接用值作为类型,不能为其他
let gender: 'male' | 'female'
// 元组(可以限制个数以及每一位元素的类型)
let arr: [string, number, boolean] = ['hello', 3090, true]
//枚举 enum
enum state {
start,
end,
}
console.log(state.start)
// 类型别名
type myType = 1 | 2 | 3 | 4 | 5
let t1: myType = 1
let t2: myType = 2
2.8 类型断言
typescript
// 类型断言 类似于类型强制转换,强制让typescript认为数据类型为指定的类型
let someValue = 'this is string'
// 尖括号语法
let strLength: number = (<string>someValue).length
// as语法
let strLength2: number = (someValue as string).length
// as const 转换为字面量
let str = 'hello' as const
// 等同于下面,不能给其他值
let str1: 'hello' = 'hello'
// 数组使用as const 会变为只读的元组
let arr1 = ['hello', 13] as const
2.9 非空断言
javascript
let str: string
str = 'abc'
// 开启strictNullChecks 为true 不允许值为空
// str = null // 不能将类型“null”分配给类型“string”
// str = undefined // 不能将类型“undefined”分配给类型“string”
// 非空断言 !
let el = document.querySelector('.box')!
// 类型断言
let el2: HTMLDivElement = document.querySelector('.box2') as HTMLDivElement
3. 函数、类、封装
3.1 函数类型
typescript
type myFun = (num1: number, num2: number) => number
function calc(a: number, b: number, fn: myFun) {
return fn(a, b)
}
// 方法一
let result = calc(1, 2, (n, m) => n + m)
// 方法二
let result2 = calc(1, 2, function (n, m) {
return n * m
})
console.log(result, result2)
// 在对象中限制函数类型
let user: {
name: string
age: number
say: () => void
}
user = {
name: '小明',
age: 10,
say: () => console.log('hai'),
}
user.say()
3.2 函数参数的灵活处理
typescript
// 可选参数 ?
const func = function (a: number, b?: number): number {
return 10
}
console.log(func(10))
console.log(func(10, 20))
// 默认参数
const func2 = (a: number, b: number = 10) => {
return 20
}
console.log(func2(10, 20))
// 可变参数
const func3 = (a: number, b: number, ...args: unknown[]): void => {
console.log(args)
}
func3(10, 20, [33, '22', true])
typescript
// 重载出现的条件:方法/函数名相同,参数类型不同 或者参数数量不同 或者参数顺序不同
function add(n: number, m: number): number
function add(n: string, m: string): string
function add(n: number, m: string): string
function add(n: string, m: number): string
function add(n: any, m: any): any {
return n + m
}
console.log(add(2, 'hello'))
console.log(add(2, 3))
3.4 类的定义
TS 中类的使用:
typescript
class Person {
name: string
age?: number
constructor(name: string, age: number = 0) {
this.name = name
this.age = age
}
say(): void {
console.log(this.name + '说话')
}
}
const p1 = new Person('张三')
const p2 = new Person('麻花', 24)
p2.say()
类的继承:
typescript
class Student extends Person {
school: string
constructor(name: string, school: string, age: number = 0) {
super(name, age)
this.school = school
}
study() {
console.log(`${this.name}在${this.school}上学`)
}
// 重写方法
say() {
console.log(this.name + '说今天天气不错!')
}
}
const s1 = new Student('西瓜', '大学', 18)
s1.study()
s1.say()
3.5 访问修饰符
typescript
class Person {
name: string
age: number
protected gender: 'male' | 'female' // protected
birthdays: string
constructor(
name: string,
age: number,
gender: 'male' | 'female',
birthdays: string,
) {
this.name = name
this.age = age
this.gender = gender
this.birthdays = birthdays
}
say(): void {
console.log(this.name + '#' + this.age)
}
}
class Student extends Person {
readonly school: string
private score: number
constructor(
name: string,
age: number,
gender: 'male' | 'female',
birthdays: string,
school: string,
score: number,
) {
super(name, age, gender, birthdays)
this.school = school
this.score = score
}
study() {
console.log(this.name + '是' + this.gender)
}
}
const p1 = new Person('西瓜', 18, 'male', '11-11')
console.log(p1)
// 属性“gender”受保护,只能在类“Person”及其子类中访问。
// p1.gender
const s1 = new Student('麻花', 18, 'female', '11-10', '高中', 98)
console.log(s1)
// 属性“score”为私有属性,只能在类“Student”中访问。
// s1.score
s1.study()
// 属性“gender”受保护,只能在类“Person”及其子类中访问。
// s1.gender
// 无法分配到 "school" ,因为它是只读属性。
// s1.school = '小学'
3.6 面向对象的封装和存取器
typescript
class Person {
name: string
private _age: number
constructor(name: string, _age: number) {
this.name = name
this._age = _age
}
set age(value: number) {
if (value >= 0 && value <= 120) this._age = value
}
get age() {
// 25岁之前返回真实年龄,
return this._age >= 25 ? this._age - 5 : this._age
}
run() {
this.left()
this.right()
this.go()
}
private left() {
console.log('迈左腿')
}
private right() {
console.log('迈右腿')
}
private go() {
console.log('Go!')
}
}
const p1 = new Person('混混', 28)
console.log(p1.age)
console.log(p1.run())
// 属性“right”为私有属性,只能在类“Person”中访问。
// console.log(p1.right());
3.7 static 的应用
typescript
class Student {
name: string
age: number
static school: string = '高中'
constructor(name: string, age: number) {
this.name = name
this.age = age
}
study() {
console.log('学!')
// 非静态方法可以访问静态资源属性
console.log(this.study.name + Student.school)
}
static run() {
console.log('跑步!')
// 静态方法可以访问静态属性
console.log(this.run.name + this.school)
// 静态方法不可以访问非静态成员属性
// console.log(this.run.name + this.age)
}
}
const s1 = new Student('麻花', 18)
console.log(s1.name)
s1.study()
// 属性“run”在类型“Student”上不存在。你的意思是改为访问静态成员“Student.run”吗?
// s1.run()
Student.run()
console.log('--------------')
console.log(Student.school)
// 什么时候使用到静态属性和方法
// 在所有对象中共同用到的同一种属性或方法,可以声明为静态属性、方法
// 常见的需要初始化使用的类 const date= new Date()
// 常见的直接使用的类 Math.random()
// 静态方法不可以访问非静态成员属性
// 非静态方法可以访问静态成员属性
3.8 抽象类与接口
typescript
// 抽象类是一种特殊的类
// 接口是一种特殊的抽象类
// 抽象类 需要使用absract 关键字申明
abstract class Person {
name: string
constructor(name: string) {
this.name = name
}
// 在一个类中没有方法体的方法,就被称为抽象方法
abstract run(): void
}
// 抽象类无法初始化: 无法创建抽象类的实例。
// const p1 = new Person('麻花')
// 如果继承抽象类的类没有实现抽象类的方法,则这个类只能称为抽象类
abstract class Children extends Person {}
// 子类继承了抽象类并实现了抽象类的方法,则子类可以正常被初始化使用
class Student extends Person {
constructor(name: string) {
super(name)
}
run() {
console.log('gogo!')
}
}
const s1 = new Student('麻花')
s1.run()
// 接口中所有方法都是抽象的,都是公有的
interface One {
// 无需给出具体的值,只是一个约束
hello: string
say: () => void
}
interface Two {}
// 可以实现继承多个接口
interface Three extends One, Two {
foo(): void
}
// 创建类来实现接口,类必须实现接口所有描述才能正常使用
class Four implements Three {
hello = 'hello'
say() {
console.log(this.hello)
}
foo = () => console.log('foo')
}
3.9 面向对象的多态性
typescript
// 由继承而产生的了相关的不同的类,对同一个方法可以有不同的响应
interface USB {
start(): void
run(): void
end(): void
}
// implements是一个类实现一个接口用的关键字.
class Mouse implements USB {
start = () => console.log('鼠标载入')
run = () => console.log('鼠标移动')
end = () => console.log('鼠标卸载')
}
class KeyBoard implements USB {
start = () => console.log('键盘载入')
run = () => console.log('键盘点击')
end = () => console.log('键盘卸载')
}
function demo(u: USB) {
u.start()
u.run()
u.end()
}
// 执行相同的方法,有不同的响应
demo(new Mouse())
demo(new KeyBoard())
3.10 接口的灵活运用
typescript
// 使用接口可以更加灵活的定义一个对象的类型
interface Person {
name: string
age: number
}
type Myobj = {
color: string
size: number
}
const p1: Person = { name: '张三', age: 17 }
const m1: Myobj = { color: 'red', size: 5 }
// 接口在后期一些灵活的用法
interface L1 {
[index: number]: string
}
const l1: L1 = { 1: 'a', 2: 'b', 3: 'c' }
interface L2 {
[key: string]: number
}
const l2: L2 = { a: 1, b: 2 }
3.11 认识和使用泛型
typescript
// 在申明的时候不指定类型,在应用的时候由应用者决定具体类型
function fun<T, W>(a: T, b: W) {
return [a, b]
}
const result = fun(1, '西瓜')
console.log(result)
3.12 泛型的灵活运用
typescript
// 泛型参数默认值
interface Person<T = number, W = string> {
id: T
name: W
}
function player<P extends Person>(user: P) {
return [user.id, user.name]
}
const result = player({ id: 333, name: 'job' })
console.log(result)
// 如果没有length属性,则不符合规范示例
interface ILength {
length: number
}
function getLength<L extends ILength>(arg: L) {
return arg.length
}
const res = getLength('abcdef')
const res1 = getLength([1, 2, 3, 4, 5])
const res2 = getLength({ name: '西瓜', age: 18, length: 18 })
console.log(res)
console.log(res1)
console.log(res2)
4.工程化中使用 TS
4.1 webpack 打包 TS
安装模块:
bash
yarn add -D webpack webpack-cli webpack-dev-server typescript ts-loader html-webpack-plugin
配置文件:
javascript
// webpack.config.js
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 设置主入口文件
entry: './src/index.ts',
output: {
filename: 'bundle.js',
path: resolve(__dirname, 'dist'),
},
module: {
// 设置以ts结尾的文件,用ts-loader 来解析,排除node_modules文件夹
rules: [{ test: /\.ts$/, use: 'ts-loader', exclude: /node_modules/ }],
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
// 告知打包工具,对以下格式的文件进行打包
resolve: {
extensions: ['.js', '.ts'],
},
mode: 'development',
}
入口文件:
typescript
// index.ts
const sub = require('./subtract')
function sum(a: number, b: number): number {
return a + b
}
console.log(sum(5, 10))
console.log(sub(20, 18))
其他模块:
typescript
// subract.ts
export = function sub(a: number, b: number): number {
return a - b
}
4.2 命名空间
typescript
// 命名空间一个最明确的目的就是解决重名问题
// 虽然函数名相同可以通过重载的方式解决,但类却不可以
namespace One {
export let school: string = '小学'
}
namespace Two {
export let school: string = '初中'
}
console.log(One.school)
// 调用其他文件命名空间,则对应文件的命名空间和变量必须已经导出
// const Three = require('./high')
// 在ts中使用require和import 需要安装@types/node模块
import { Three } from './high'
console.log(Three.school)
其他模块:
typescript
export namespace Three {
export let school: string = '高中'
}
4.3 vue3 中使用 TS
按照脚手架步骤,对项目进行初始化即可
// TODO