参考: 小满TypeScript基础教程全集(完结)_哔哩哔哩_bilibili
学习TypeScrip1(基础类型)_小满typescript基础教程全集-CSDN博客
介绍安装,基础类型
介绍安装
// 介绍:
js的超集
// 安装
前置环境:node
安装ts
cnpm i typescript -g
检查是否安装,版本号
tsc -v // Version 5.3.3
生成 tsconfig.json 配置文件
tsc --init
创建1.ts
let str: string = 'vv'
console.log(str);
// 第一种方法
编译成js文件
tsc -w // -w即--watch
输出
node 1.js
// 第二种方法
安装ts-node
cnpm i -g ts-node
ts-node 1.ts
基础类型
// string,num,boolean,null,undefined,void,Symbol,bignit
// string
let str: string = "vv"
// num
let num1: number = 123
// NaN,Infinity,十六进制如0xf00d,二进制0010,八进制0o744
// boolean
let boo1: boolean = true
let boo2: boolean = Boolean(0)
// null,undefined
let n: null = null
let un: undefined = undefined
// void空值,多用于无返回的函数
let v1: void = null // 严格模式true报错,false可用
let v2: void = undefined
// void和undefined,null的区别
undefined,null可以赋值给其它类型,void不行
// undefined和null在非严格模式可以互相赋值
基础类型
1. 基本数据类型
Boolean Number String object symbol bigint
let sym: symbol = Symbol("me")
let bi: bigint = 100n // 编译错误,bigint是es11语法,需要配置tsconfig
任意类型:any,unknow
// any,unknow都是顶级类型
let an: any = 1
an = '2'
an = true
let un: unknown = 1
un = '2'
un = true
// 区别1:unknown只能赋值给unknown和any;any可以
let un1: unknown = 1
let un2: string = un1 // error,不能将类型“unknown”分配给类型“string”。ts(2322)
let un3: any = un1
// 区别2:unknow没办法读属性;any可以
let o1: unknown = { age: 1 }
console.log(o1.age); // err,“o1”的类型为“未知”。ts(18046)
// 总结:unknown比any更安全,any的弊端是失去了ts的检测作用
object,Object,{}
// Object
let o1: Object = 1
let o2: Object = '1'
let o3: Object = true
let o4: Object = {}
let o5: Object = []
let o6: Object = () => 1
let o7: Object = null // 不能将类型“null”分配给类型“Object”。ts(2322)
let o8: Object = undefined // 不能将类型“undefined”分配给类型“Object”。ts(2322)
// object,原始类型报错
let o1: object = {}
let o2: object = []
let o3: object = () => 1
// {},即new Object等同于Obeject
// 注:
// 为对象直接添加新属性时会报错-->泛型
let o1: Object = {}
o1.age = 1
console.log(o1);
interface:接口和对象类型
// - 1.使用接口约束的时候不能多也不能少属性,必须保持一致
interface Person {
name: string,
age: number
}
const vv: Person = {
name: "vv"
}
// 类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性
// - 2.可选,只读
interface Person {
readonly id: number, // 只读
name: string,
age?: number // 可选
}
const vv: Person = {
id: 1,
name: "vv"
}
vv.id = 2
// 无法为“id”赋值,因为它是只读属性。ts(2540)
// - 3.任意属性(索引签名)
// 常用:后端接口只取需要字段
interface Person {
readonly id: number,
name: string,
[prop: string]: any
}
const vv: Person = {
id: 1,
name: "vv"
}
// - 4.重名,继承
// 重名会合并
// 继承extends
interface A {
name: string,
}
interface B extends A {
id: number
}
const vv: B = {
id: 1,
name: "vv"
}
// - 5.对象中的函数
interface Obj {
name: string,
cb: () => number
}
const o1: Obj = {
name: "a",
cb() { return 1 }
}
o1.cb()
// - 6.函数
interface Fn {
(name: string): number
}
const f: Fn = (name) => 1
f('vv')
数组Array
// - 1.定义
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
// - 2.对象数组
interface Aperson {
name: string,
[prop: string]: any
}
let arr1: Aperson[] = [{ name: 'a' }, { name: "b" }]
// - 3.二维数组
let arr1: number[][] = [[1], [2]]
let arr2: Array<Array<number>> = [[1], [2]]
// - 4.大杂烩数组
let arr1: any[] = [1, '2', true]
函数
// - 1.定义
function f1(x: number, y: number): number {
return x + y
}
const f2 = (x: number, y: number): number => {
return x + y
}
// - 2.默认参数
function f1(x: number, y: number = 20): number {
return x + y
}
// - 3.可选
function f1(x: number, y?: number): number {
return x + y // 24年2月25日,严格模式下会报错:“y”可能为“未定义”。ts(18048)
}
// - 4.参数为对象时
interface Obj1 {
id: number,
name?: string
}
function f1(ob: Obj1): number {
return 1
}
// - 5.剩余参数,arguments
function f1(...args: any[]): number {
console.log(arguments);
let a: IArguments = arguments
return 1
}
f1(1, '2', true)
// arguments是伪数组,没有相应方法,用any[]会提示用IArguments
// - 6.函数重构
// 函数名称相同,根据不同参数数量或类型做不同吹
function fn(params: number): number
function fn(params: string): string
function fn(params: string, params2: boolean): number[]
function fn(params: any, params2?: boolean): any {
if (params2) {
return [1, 2, 3]
} else {
if (typeof params === "number") {
return 1
} else {
return "2"
}
}
}
console.log(fn("1", true))
联合类型,交叉类型,类型断言
// - 1.联合类型
let i: number | string
// - 2.交叉类型
interface A {
name: string;
age: number;
}
interface B {
sex: string
}
let v: A & B = {
name: 'v',
age: 1,
sex: 'man'
}
// - 3.类型断言as
// 一
const fn = (x: number | string) => x.length
// err,类型“string | number”上不存在属性“length”。类型“number”上不存在属性“length”。ts(2339)
// 改为
const fn = (x: number | string) => (x as string).length
console.log(fn("123"))
// 二
interface A {
run: string
}
interface B {
build: string
}
const fn = (type: A | B): string => {
return (type as A).run
}
// 三,使用any断言赋值
window.abc = 123 // 类型“Window & typeof globalThis”上不存在属性“abc”。ts(2339)
(window as any).abc = 123
// 注意:类型断言只能欺骗编译器,不会改变结果
内置对象
- 1.内置对象
// String,Number,Boolean,Date,RegExp,Error
let boo1: Boolean = new Boolean(1)
- 2.dom对象,bom对象
// dom对象:常见的HTML(元素名称)Element,HtmlElement,Element
let odiv = document.querySelector('div') as HTMLDivElement
let aDiv: NodeList = document.querySelectorAll('div')
// bom对象:
let loc: Storage = localStorage
let loca: Location = location
let coo: string = document.cookie
let promise: Promise<number> = new Promise<number>((resolve, reject) => {
resolve(1)
})
问题:下面的代码在JS中可以运行,在TS中会编译错误:参数“x”隐式具有“any”类型
function fn(x, y) {
return x + y
}
console.log(fn(1, 2)); // 3
console.log(fn('1', '2')); // '12'
思路:
type T = number | string
function fn(x: T, y: T) {
if (typeof x === 'string' || typeof y === 'string') {
return x.toString() + y.toString();
} else {
return x + y;
}
}
console.log(fn(1, 2)); // 3
console.log(fn('1', '2')); // '12'
但是仍然存在问题:
const str = fn('aaa','bbb')
str.split('') // TS(2339),类型“number”上不存在属性“split”
解决方法:函数重载
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any { return x + y }
类
- 1.涉及知识点
// class的基本用法,约束implement,继承extends
// 只读readonly
// private:私有的,只能内部使用,子类和new的实例都无法使用
// protected:内部,子类可用
// public:共有的
// static:静态方法,Vue上的方法,如Promise.all等 注:static只能调用static中的,内部的属性方法
// super原理:指向父类原型链
// get,set方法:类似defineProperty
interface Opt {
el: string | HTMLElement
}
interface VueCls {
options: Opt
init(): void
}
interface Vnode {
tag: string,
text?: string,
children?: Vnode[]
}
class Dom {
private createElement(el: string) {
return document.createElement(el)
}
protected setText(el: HTMLElement, text: string | null) {
el.textContent = text
}
protected render(data: Vnode) {
const root = this.createElement(data.tag)
if (data.children && Array.isArray(data.children)) {
data.children.forEach(e => {
const child = this.render(e)
this.setText(child, e.text ?? null)
root.appendChild(child)
})
} else {
this.setText(root, data.text ?? null)
}
return root
}
}
class Vue extends Dom implements VueCls {
constructor(options: Opt) {
super()
this.options = options
this.init()
}
readonly options: Opt
static version() {
return '1.1.1'
}
public init(): void {
let data: Vnode = {
tag: "div",
children: [{
tag: "section",
text: "节点1"
}, {
tag: "section",
text: "节点2"
}, {
tag: "section",
text: "节点3"
},]
}
let app = typeof this.options.el == 'string' ? document.querySelector(this.options.el) : this.options.el;
app?.append(this.render(data))
}
}
new Vue({
el: '#app'
})
抽象类(基类)
- 1.抽象类不能实现
abstract class A {
abstract init() {
console.log(1); // 方法“init”不能具有实现,因为它标记为抽象。ts(1245)
}
}
new A() // 无法创建抽象类的实例。ts(2511)
- 2.派生
// 常用于:给其他类继承
abstract class A {
name: string
constructor(name?: string) {
this.name = name as string
}
abstract init(name: string): void
getName() {
return this.name
}
}
class B extends A {
constructor() {
super()
}
init(name: string): void { }
setName(name: string) {
this.name = name
}
}
const bbb = new B()
bbb.setName("vv")
console.log(bbb.getName())
元祖类型Tuple
- 1.定义:数组的变种
const arr: [number, boolean] = [1, true]
- 2.只读
const arr: readonly [number, boolean] = [1, true]
arr[0] = 2 // 无法为“0”赋值,因为它是只读属性。ts(2540)
- 3.可选
const arr: [number, boolean?] = [1]
枚举enum
- 1.数字枚举
// 从0开始
enum Color {
red,
green,
blue,
}
console.log(Color.red, Color.green, Color.blue) // 0,1,2
// 增长枚举
enum Color {
red = 2,
green,
blue,
}
console.log(Color.red, Color.green, Color.blue)
- 2.字符串枚举
enum Color {
red = 'red',
green = 'green',
blue = 'blue',
}
- 3.异构枚举(不同类型的枚举)
enum Color {
red,
green = '1',
}
- 4.接口枚举
interface A {
name: Color.red
}
enum Color {
red,
green = '1',
}
const v1: A = {
name: 0
}
- 5.const声明枚举
// tsc编译为js文件,const声明的枚举会被编译成常量,普通声明的枚举编译完后是个对象
const enum Type {
'eat' = 1, 'drink', 'play'
}
- 6.反向映射(由value推key)
enum Type {
"eat",
"drink",
"play",
}
let type = Type[2]
console.log("🚀 ~ type:", type)
类型推断,类型别名
- 1.类型推断
let num = 1 // let num: number
num = '2' // 不能将类型“string”分配给类型“number”。ts(2322)
- 2.类型别名
// 正常使用,定义类型别名
type n = number | string
let num: n = 1
num = '2'
num = true // 不能将类型“boolean”分配给类型“n”。ts(2322)
// 定义函数别名
type n = () => string
let fn: n = () => 'vv'
// 和interface的区别
// 高级用法
// extends:包含, 左边是右边的子类型
type a1 = 1 extends number ? 1 : 0
type a2 = 1 extends Number ? 1 : 0
type a3 = 1 extends any ? 1 : 0
type a4 = 1 extends unknown ? 1 : 0
type a5 = 1 extends Object ? 1 : 0
type a7 = 1 extends never ? 1 : 0 // 0
// 顶级类型知识:any unknow > Object > Number String... > number > 1 true >never
never
- 1.表示不存在的状态
- 2.基本使用
type A = string & number // never
function fn(): never {
throw new Error("err")
}
function fn2(): never {
while (true) { }
}
- 3.应用场景
type A = "唱" | "跳" | "rap" | '篮球'
function fn(params: A) {
switch (params) {
case "唱":
break
case "跳":
break
case "rap":
break
default:
// 兜底逻辑
const error: never = params // type A添加篮球,报错
break
}
}
Symbol
- 1.
const v1: symbol = Symbol(1)
const v2: symbol = Symbol(1)
console.log(v1, v2)
console.log(v1 === v2) // false
- 2.
console.log(Symbol.for("v3") === Symbol.for("v3")) // true
- 3.使用场景:用作对象的键名
const v1: symbol = Symbol(1)
const v2: symbol = Symbol(1)
let obj1 = {
name: "v",
[v1]: 1,
[v2]: 2,
}
// 只能拿到name数据
for (const key in obj1) {
console.log(key)
}
console.log(Object.keys(obj1))
console.log(Object.getOwnPropertyNames(obj1))
// 只能拿到symbol数据
console.log(Object.getOwnPropertySymbols(obj1))
// 解决方法
console.log(Reflect.ownKeys(obj1))
- 4.生成器
function* fn() {
yield Promise.resolve('v1')
yield 'v2'
}
const v = fn()
console.log(v) // Object [Generator] {}
console.log(v.next()) // { value: 'v1', done: false }
console.log(v.next()) // { value: 'v2', done: false }
console.log(v.next()) // { value: undefined, done: true }
- 5.迭代器
const s1 = new Set([1, 1, 2, 2])
const m1 = new Map()
m1.set([1,2,3], "v1")
// s1,m1,querySelectorAll,arguments都可以使用for of,∵它们都实现了Symbol.iterator方法
// 手写forof
const each = (value: any) => {
let n: any = value[Symbol.iterator]()
let next: any = { done: false }
while (!next.done) {
next = n.next()
if (!next.done) {
console.log(next.value)
}
}
}
// 等价于
for (const iterator of s1) {
console.log(iterator);
}
- 6.解构
// ...解构的原理也是iterator
let [a, b, c] = [1, 2, 3]
- 7.对象实现Symbol.iterator实现forof
const obj1 = {
max: 5,
[Symbol.iterator]() {
return {
name: this.name,
max: this.max,
next() {
if (this.max === 0) {
return {
done: true,
value: undefined,
}
} else {
return {
done: false,
value: this.max--,
}
}
},
}
},
}
泛型
- 1.问题引入
// 这样使用函数过于麻烦
function fn1(x: string, y: string): string[] {
return [x, y]
}
function fn2(x: number, y: number): number[] {
return [x, y]
}
// 改为
function fn<T>(x: T, y: T): T[] {
return [x, y]
}
// 或
function fn<A, B>(x: A, y: B): (A | B)[] {
return [x, y]
}
// 泛型就相当于动态类型
- 2.type和interface使用泛型
type A<T> = number | T
const a: A<boolean> = 1
interface Obj1<T> {
msg: T
}
const res: Obj1<string | number> = {
msg: 200,
}
- 3.实际应用:axios
const axios = {
get<T>(url: string): Promise<T> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(JSON.parse(xhr.responseText))
}
}
xhr.send(null)
})
},
}
interface Data {
code: number
msg: string
}
axios.get<Data>("./data.json").then((res) => {
console.log(res)
})
- 4.泛型约束
// 1
interface Obj2 {
length: number
}
function fn<T extends Obj2>(params: T) {
return params.length
}
fn("111")
fn([1, 1, 1])
fn(111) // 类型“number”的参数不能赋给类型“Obj2”的参数。ts(2345)
// 2
function fn<T extends number>(x: T, y: T) {
return x + y
}
fn(1, 2)
- 5.keyof
// 返回对象的一个值
let obj = {
name: "v",
age: 11,
}
function rt<T extends object, K extends keyof T>(obj: T, prop: K) {
return obj[prop]
}
console.log(rt(obj, "age"))
console.log(rt(obj, "sex")) // 类型“{ name: string; age: number; }”上不存在属性“sex”。ts(2339)
// 遍历一个interface将属性加上?可选或者readonly
interface A {
name: string,
age:number
}
type Opt<T extends object> {
[key in keyof T]?:T[key]
}
type B = Opt<A>
tsconfig.json的配置
namespace命名空间
- 1.使用
namespace A {
export const a = 1
export const fn = () => 2
}
console.log(A.a, A.fn())
// 可以嵌套写法
// 可以同名合并
- 2.模块化
// 1.ts中export
export namespace A {
export const a = 1
export const fn = () => 2
}
// 2.ts中import导入
import {A} from '../1.'
- 3.常见案例:跨端
namespace ios {
export const fn = () => {
console.log(1)
}
}
namespace android {
export const fn = () => {
console.log(2)
}
}
模块解析
- 1.常见
commonjs-->nodejs环境
cmd-->requirejs
amd-->seajs
umd-->amd和commonjs的合体
- 2.es模块化
// 默认导入
export default 1
-
import x from "./1"
console.log(x)
// 分开导入
export const a = 1
export const b = 2
-
import { a, b } from "./1"
console.log(a, b)
// import * as num from "./1" // 或用* as 全部导入
// as起别名
// 动态引入
if (true) {
import("./1").then((res) => {})
}
声明文件d.ts
import axios from "axios"
import express from "express"
// 在同时安装axios和express时,express提示报错,∵express确少declare声明文件,可npm i @type/包名安装,或自行编写d.ts文件
mixin混入
// 对象的混入
// 类的混入
decorator装饰器
// 类装饰器ClassDecorator
// 装饰器工厂
// 方法装饰器MethodDecorator
// 属性装饰器PropertyDecorator
// 参数装饰器ParameterDecorator
webpack构建v3+ts流程
- 1.
//
tsc --init生成tsconfig.json
npm init -y生成package.json
//
新建webpack.config.js,index.html,src/App.vue和main.ts和shim.d.ts(垫片)
//
tsconfig.js中加上范围include
{
"compilerOptions": {},
"include": ["src/**/*"]
}
- 2.配置webpack
//
安装webpack相关依赖: cnpm i webpack webpack-cli -D
安装webpack本地起服务:npm i webpack-dev-sever -D
// 修改package.json
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server"
},
// 编写webpack.configuration.js,并build打包
const { Configuration } = require("webpack")
const path = require("node:path")
/**
* @type {Configuration}
*/
module.exports = {
mode: "development",
entry: "./src/main.ts",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
}
- 3.支持ts
cnpm i typescript -D
cnpm i ts-loader -D
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
use: ["ts-loader"],
},
],
},
}
- 4.支持vue
cnpm i vue
cnpm i vue-loader -D
cnpm i html-webpack-plugin -D
// main.ts
import { createApp } from "vue"
import App from "./App.vue"
createApp(App).mount("#app")
// index.html
<div id="#app"></div>
// App.vue
<template>1</template>
// 问:怎么把html和main.ts联系起来
const HtmlWebpackPlugin = require("html-webpack-plugin")
const { VueLoaderPlugin } = require("vue-loader")
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader",
},
{
test: /\.vue$/,
use: "vue-loader",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
}),
new VueLoaderPlugin(),
],
// 无法识别vue文件,需要垫片
declare module "*.vue" {
import { DefineComponent } from "vue"
const component: DefineComponent<[], {}, any>
export default component
}
// 打包成功
- 5.vue中支持ts
<script setup lang="ts">
</script>
// webpack.config中
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
},
},
},
{
test: /\.vue$/,
use: "vue-loader",
},
],
},
- 6.样式
cnpm i style-loader -D
cnpm i css-loader -D
cnpm i sass sass-loader -D
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
- 7.优化:模块拆分
// 以安装了moment组件为例
cnpm i moment
app中使用,打包后都挤在一个js文件中
// 修改webpack.config
module.exports = {
output: {
path: path.resolve(__dirname, "dist"),
filename: "[chunkhash].js",
clean: true,
},
optimization: {
splitChunks: {
cacheGroups: {
moment: {
name: "moment",
chunks: "all",
test: /[\\/]node_modules[\\/]moment[\\/]/,
},
common: {
name: "common",
chunks: "all",
minChunks: 2, // 引用次数大于2的拆分
},
},
},
},
}
- 8.不用style-loader
cnpm i mini-css-extract-plugin -D
// webpack
const CssExtractPlugin = require("mini-css-extract-plugin")
module: {
rules: [
{
test: /\.ts$/,
use: {
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
},
},
},
{
test: /\.vue$/,
use: "vue-loader",
},
{
test: /\.css$/,
use: [CssExtractPlugin.loader, "css-loader"],
},
{
test: /\.scss$/,
use: [CssExtractPlugin.loader, "css-loader", "sass-loader"],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./index.html",
}),
new VueLoaderPlugin(),
new CssExtractPlugin(),
],
发布订阅模式
已放到设计模式章节中
set map weakSet weakMap
- 1.set
const set: Set<number> = new Set([1, 2, 2, 3, 3, 3])
// add,delete,clear
// set.add(4)
// set.delete(1)
// set.clear()
// console.log(set)
// has
// console.log(set.has(1))
// entries,forEach,forof遍历都可以
- 2.map
const map: Map<any, any> = new Map()
// set,delete,clear
// map.set(1, 11)
// map.set(2, 22)
// map.delete(1)
// map.clear()
// console.log(map)
// map,has
// console.log(map.get(1))
// console.log(map.has(1))
// entries,forEach,forof遍历都可以
/**
* 3,weakMap,weakSet
*
* 弱引用
* weakMap,weakSet的键只能是引用类型
* 会被垃圾回收机制自动回收,V8 GC大约200ms延迟
*/
proxy,Reflect
/**
* 1,proxy
*
* 注:只能代理引用类型,handler对象13种方法,拦截get,set,函数,in,forin等等
*/
/**
* 2,Reflect
*
* 常用:
* Reflect.get(target, name, receiver)
* Reflect.set(target, name,value, receiver)
* 注:receiver就相当于target,是处理箭头函数情况下的值
*/
// 演示
const person = { name: "v", age: 10 }
const personProxy = new Proxy(person, {
get(target, prop, receiver) {
if (target.age <= 18) {
const res = Reflect.get(target, prop, receiver)
return "未成年:" + res
} else {
return "成年了"
}
},
set(target, prop, value, receiver): boolean {
return Reflect.set(target, prop, value, receiver)
},
})
console.log(personProxy.age)
/**
* 3,用proxy + Reflect实现vue的Observer函数
*/
const list: Set<Function> = new Set()
const autoRun = (cb: Function) => {
if (!list.has(cb)) {
list.add(cb)
}
}
const observer = <T extends object>(params: T) => {
return new Proxy(params, {
set(target, prop, value, receiver) {
const res = Reflect.set(target, prop, value, receiver)
list.forEach((e) => e())
return res
},
})
}
const personProxy = observer({ name: "v" })
autoRun(() => {
console.log("变化了")
})
personProxy.name = "nn"
类型守卫,类型拓宽\类型缩小
/**
* 1,类型收缩
*
* typeof的缺点:对象数组null都返回object
*/
const isStr = (str: any): str is string => typeof str === "string"
const isNum = (num: any): num is number => typeof num === "number"
const isArr = (arr: any) => arr instanceof Array
const isObj = (obj: any) => ({}.toString.call(obj) === "[object Object]")
const isFn = (fn: any) => typeof fn === "function"
/**
* 2,类型守卫
*
* 案例:实现一个函数,传入如何类型
* 如果是一个对象,就检查里面的属性
* 属性为number就取两位
* 属性为string就去除空格
* 如果是函数就执行
*/
/**
* 问题1:this的指向出现问题,this.a报错
* 浏览器环境指向window,node环境指向undefined
*
* 问题2:toFixed,trim等代码提示取消了,∵val的类型推断为any
* 类型守卫:返回值为boolean时
* const isStr = (str: any): str is string => typeof str === "string"
* const isNum = (num: any): num is number => typeof num === "number"
*/
const fn = (params: any) => {
if (isObj(params)) {
Object.keys(params).forEach((key) => {
const val = params[key]
if (isNum(val)) {
params[key] = val.toFixed(2)
} else if (isStr(val)) {
params[key] = val.trim()
} else if (isFn(val)) {
params[key]()
}
})
}
}
const obj = {
a: 10000.2222,
b: " v ",
c: function () {
console.log(this)
return this.a
},
}
console.log(fn(obj))
协变,逆变
/**
* 1,协变,鸭子类型
*/
interface A {
name: string
age: number
}
interface B {
name: string
age: number
sex: string
}
let a: A = {
name: "a",
age: 1,
}
let b: B = {
name: "b",
age: 2,
sex: "man",
}
a = b
/**
* 2,逆变
*
* 看似是反过来的,其实本质上调用的就是fna才是对的
*/
interface A {
name: string
age: number
}
interface B {
name: string
age: number
sex: string
}
let fna = (params: A) => {}
let fnb = (params: B) => {}
fnb = fna
/**
* 3,双向逆变
*
* tsconfig.json中strictFunctionTypes:false
*/
泛型工具一
/**
* 1,Partial:属性全部可选
*/
// 使用
interface A {
name: string
age: number
sex: string
}
type PartialA = Partial<A>
// type PartialA = {
// name?: string | undefined
// age?: number | undefined
// sex?: string | undefined
// }
// 原理
type myPartial<T> {
[P in keyof T]?:T[P]
}
type PartialA = myPartial<A>
/**
* 2,Required:全选
*/
// 使用
interface A {
name?: string
age?: number
sex?: string
}
type requireA = Required<A>
// 原理
type myRequired<T> {
[P in keyof T]-?:T[P]
}
type requireA = myRequired<A>
/**
* 3,Pick:提取部分属性
*/
// 使用
interface A {
name: string
age: number
sex: string
}
type pickA = Pick<A, "age" | "name">
// 原理
type myPick<T, K extends keyof T> = {
[P in K]: T[P]
}
type pickA2 = myPick<A, "age" | "name">
/**
* 4,Exclude排除部分属性
*/
// 使用
type excludeA = Exclude<number | string | boolean, string>
// 原理
type myExclude<T, K> = T extends K ? never : T
type excludeA2 = myExclude<number | string | boolean, string>
/**
* 5,Omit:排除interface中不需要的部分
*/
interface A {
name: string
age: number
sex: string
}
type omitA = Omit<A, "age" | "name">
// 原理:结合Exclude和Pick
type myOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type omitA2 = myOmit<A, "age" | "name">
泛型工具二
/**
* 1,Record:约束对象的key和value
*
* key不能少,value不能少
*
* 支持嵌套写法
*/
// 使用
type Key = "c" | "x" | "k"
type Value = "唱" | "跳" | "rap" | "篮球"
const obj: Record<Key, Value> = {
c: "唱",
x: "跳",
k: "rap",
}
// 原理
// 对象的key只能是string number symbol
// 语法糖 string | number | symbol === keyof any
type myRecord<T extends keyof any, K> = {
[P in T]: K
}
const obj2: myRecord<Key, Value> = {
c: "唱",
x: "跳",
k: "篮球",
}
/**
* 2,ReturnType:获取函数的返回值
*/
// 使用
const fn = () => [1, true]
type rt = ReturnType<typeof fn>
// 原理
type myReturnType<F extends Function> = F extends (...arg: any[]) => infer Res
? Res
: never
type rt2 = myReturnType<typeof fn>
5. 元祖Tuple
2. 解构
元素较多时,不方便使用下标来访问
let stu: [string, number] = ['xiaobai', 10]
let [user, age] = stu
console.log(user, age); // xiaobai 10
3. 剩余元素
let tu:[string,...number[]]
tu = ['1',2,2,2]
类型断言
有时我们比TS更清楚的知道某个变量的类型,∴我们希望手动指定一个值的类型
const arr: number[] = [1, 2, 3, 4];
const a: number = arr.find(num => num > 2) // 提示 ts(2322)不能将类型“undefined”分配给类型“number”
const a: number = arr.find(num => num > 2) as number
非空断言
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。即x!的值不会为null或undefined
let a: number | undefined | null
a.toString() // TS(2533)对象可能为 "null" 或“未定义”
a!.toString()
确定赋值断言
定义了a,没有赋值就使用了,报错
let a:number
console.log(a); // TS(2534)在赋值前使用了变量“a”
!告诉TS这个属性会被赋值
let a!: number
console.log(a); // undefined
字面量类型
// 字符串字面量类型,数字字面量类型,布尔字面量类型
let x: 'string' = 'string';
let y: 1 = 1
let z: true = true;
注意:string类型是马,'string'字面量类型是黑马.所有'string'字面量类型可以给string类型赋值,反之则不行
let str1: 'hello world' = 'hello world'
let str2: string = 'nihao'
str2 = str1
str1 = str2 // TS(2322)不能将类型“string”分配给类型“"hello world"”
const,let分析 const定义时,在缺省类型注解时,TS推断出它的类型由赋值字面量类型决定 let定义时,在缺省类型注解时,转换为了赋值字面量类型的父类型--type widening
{
const str = '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
}