.ts 和 .tsx 后缀
1、.tsx = ts + jsx 语法;
2、.ts = ts ;
3、webpack 中可以配置,.ts = ts + jsx ;
type(类型) 和 class(类)
1、type: 数据类型 string/number/boolean/symbol/bigint null/undefined object
2、typeof 返回 type
typeof 函数,返回 function;
typeof null,返回 object;
3、class
JS 面向对象的编程:基于 class 关键字的面向对象编程、基于原型的面向对象编程。
可见性修饰符:控制 class 的方法或属性对于 class 外的代码是否可见。包括 public/protected/private 1、public:表示公有的,公有成员可以被任何地方访问,默认值,可省略。
2、protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
class Animal {
protected move(){
console.log('移动')
}
run(){
this.move()
console.log('跑')
}
}
const a = new Animal()
a.move() //报错
class Dog extends Animal{
bark(){
this.move()
console.log('叫')
}
}
const d = new Dog()
d.move() //报错
3、private:表示私有的,只在当前类中可见,对实例对象和子类不可见。
class Animal {
private __run__(){
console.log('Animal 内部辅助函数')
}
protected move(){
this.__run__()
console.log('移动')
}
run(){
this.__run__()
this.move()
console.log('跑')
}
}
const a = new Animal()
a.move() //报错
a.__run()__ //报错
class Dog extends Animal{
bark(){
this.__run__() //报错
this.move()
console.log('叫')
}
}
const d = new Dog()
d.move() //报错
d.__run__() //报错
ts 的好处
1、报错
function add(a:number, b:number){return a + b}
add('1' //写代码时就会检查是否有错误,不需要等到编译执行
2、提示,ts 会自动进行类型匹配
语法
const a:undefined = undefined
const b:null = null
const c:string = 'hi'
const d:boolean = false
const e:number = 3
const f:symbol = Symbol('hi')
const g:bigint = 123n
//const obj:object = {} 小写 object 是类型
//const obj:Object = {} 大写 Object 是类,因为Object 有更加详细的类,一般不这样写,用下面的写法
const arr:Array<string | number | null> = [1,2,'3',null]
//函数
//1. 类型写在函数体,输入类型、输出类型
const add1 = (a:number,b:number):number => a + b
//2. :type
const add2:(a:number,b:number) =>number = (a,b) => a + b
//3. type 缩写
type Add = (a:number,b:number) => number
const add3: Add = (a,b) => a + b
//4. 有属性,只能用 interface
interface AddWithProps {
(a:number,b:number):number
xxx:string
}
const add4:AddWithProps = (a,b) => a + b
add4.xxx = '123'
ts 独有的
any:任意类型;
unknown:一般是从网络上获取到的数据,现在不知道是什么类型,但是使用时需要明确类型,可以用断言;
void:没有类型,一般用在函数里面,表示没有返回值;
never:可以用集合理解,当交集为空时,类型就是 never,代码中出现 never 时,一般就是出问题了;
enum:枚举,可以为一组数值赋予名字,默认从 0 开始,枚举的好处是可以由枚举的值得到它的名字;
元祖:表示一个已知元素数量和类型的数组,每一个的类型都要写清楚;
readonly:只能修饰属性,不能修饰方法,修饰的属性需要手动提供明确的类型;
let a:any = 'hi'
let b:unknown = JSON.parse('{"name":"jason"}')
type C = {name:string}
console.log((b as C).name) //断言,使用时声明类型
let fn:()=>void = function(){
console.log(1)
}
type Dir = 1 | 2 | 3 | 4 | undefined
let dir:Dir
switch(dir){
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case undefined:
break;
default:
console.log(dir) //dir 的类型为 never
break;
}
type X = number & string //交集为空,X 的类型 never
type Y = (1|2|3) & (2|3|4) //Y 类型 (2|3)
let p:[number,string,boolean] = [1,'33',true]
enum Dir2 {东,南,西,北}
let d:Dir2 = Dir2.东 //0
let e:Dir2 = Dir2.北 //3
console.log(d) //0
//枚举可以用以下写法代替
type Dir2 = '东' | '南' | '西' | '北'
let d:Dir2 = Dir2.东
let d:Dir2 = Dir2.北
联合类型 |
1、如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员;
2、类型区分:如果是简单类型,可以用 typeof 做类型区分;复杂类型的话,需要被联合起来的类型有一个相同的 key ,然后用 if、switch 判断做分支选择,tagged type
type A = {
name:'a';
age:number;
}
type B = {
name:'b';
gender:string;
}
const fn1 = (n:A | B)=>{
console.log(n.name) //ok
console.log(n.gender) //报错
}
const fn2 = (n: number | B)=>{
if(typeof n === 'number'){
n.toFixed()
}else{
n.name
}
}
const fn3 = (n:A | B)=>{
if(n.name === 'a'){
n.age
}else{
n.gender
}
}
交叉类型 &
不能用于简单类型之间,一般用于 object 之间;
& 表示同时拥有这些属性,当属性的 key 相同时,类型也要一样,如下:两个 age 的类型一样,都是 number;
type A = number & string //A 为 never
type B = {name:string,age:number} & {age:number,gender:string}
let a:B = {
name:'jason',
age:18,
gender:'男'
}
div 的类型
const d:HTMLDivElement= document!.getElementById('xxx') as HTMLDivElement
const value = (document!.getElementById('xx')! as HTMLInputElement).value
const f = (x:number) => x+1
f(parseInt(value))
泛型
将类型用变量表示,可以将泛型理解为函数调用,尖括号用圆括号代替,主要是支持不同的类型。
type F<T> = T | [T]
type FNumber = F<number>
type FString = F<string>
type Add<T> = (a:T,b:T) => T
const addN:Add<number> = (a,b) => a + b
const addS:Add<string> = (a,b) => a + b
- 泛型约束
因为泛型的类型太多,参数的属性可能只有某些类型上才有,比如数组的 length 属性,其它的类型可能没有这个属性,此时,需要给泛型添加约束:
interface Length{
length:number
}
function id<T extends Length>(value:T):T{
console.log(value.length)
return value
}
//或者
function id<T>(value:T[]):T[]{
console.log(value.length)
return value
}
- 多个泛型变量
function getProp<T,K extends keyof T>(obj:T,key:K){
return obj[key]
}
let person = {name:'jack',age:18}
getProp(person,'name')
keyof 关键字接受一个对象类型,生成其键名的联合类型:name | age。
- 泛型工具 1、索引签名类型:
无法确定对象中有哪些属性时使用; key 是占位符,可用任意词。
interfac AnyObject{
[key:string]:number
}
2、Record<keys,T>:
构造一个对象类型,属性键为 keys ,属性类型为 T
type RecordObj = Record<'a' | 'b' | 'c',string[]>
let obj:RecordObj = {
a:['1'],
b:['2'],
c:['3']
}
3、映射类型:只能在类型别名中使用,不能在接口中使用。
type PropKeys = 'x' | 'y' | 'z'
type Type1 = {x:number; y:number; z:number}
//使用映射类型
type PropKeys = 'x' | 'y' | 'z'
type Type2 = {[Key in PropKeys]:number}
-------------------
type Props = {a:number; b:string; c:boolean}
type Type3 = {[Key in keyof Props]:number}
Key in PropKeys表示 Key 可以是 PropKeys 联合类型中的任意一个,类似于 for{let key in obj}
Partial<Type>的实现
type Partial<Type> = {
[P in keyof Type]?: Type[p]
}
type Props = {a:number; b:string; c:boolean}
type PartialProps = Partial<Props>
- 索引查询类型:用来查询属性的类型
Type[p]表示获取 Type 中每个键对应的类型。
type Props = {a:number; b:string; c:boolean}
type A = Props['a' | 'b'] // number | string
type B = Props[keyof Props] // number | string | boolean
递归类型
type A = Array<number | A >
const b:A = [1,[2],[2,[3]],[[[4]]],[[[[5]]]]]
const a:Array<number> = [1,2,3]
a.push()
type Tree = {
id:string | number;
children:Tree[]
}
var tree = {
id:1,
children:[
{id:2,children:[]}
]
}
类型声明文件:.d.ts文件
- 为已有 js 文件提供类型声明: 1、在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。
2、declare:用于类型声明,为其它地方(比如:.js 文件)已存在的变量声明类型,而不是创建一个新的变量。
3、对于 type/interface 等明确是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
4、对于 let/function 等具有双重含义(在 TS、JS 中都能用),应该使用 declare 关键字,明确指定此处用于类型声明。
ts.config.json 常见配置项
重载
让一个函数满足多种参数条件,常见方法使用:typeof、length
function add(a:number,b:number):number ;
function add(a:string,b:string):string;
function add(a:any,b:any):any {
if(typeof a === 'number' && typeof b === 'number'){
return a + b
}else{
return a + '' + b
}
}
add(1,2)
add('hello','world')
type Options = {header?:any}
function get(url:string,options:Options):void
function get(options:Options & {url:string}):void
function get(url:string | (Options & {url:string}),options?:Options):void{
if(arguments.length === 1){
const myOptions = url as {url:string} & Options
myOptions.url
}else{
console.log(url as string)
}
}
泛型封装网络请求
type user = {
id:string | number;
name:string;
age:number;
}
type Response<T> = {
data:T
}
type Y = Partial<user> //给所有的 key 加问号
type O = Omit<user,'id'> //省略 id
type T =Partial<Omit<user,'id'>>
type CreateResource = (path:string) =>{
create:(attrs:Omit<Partial<user>,'id'>) =>Promise<Response<user>>
delete:(id:user['id']) => Promise<Response<never>>
update:(id:user['id'],attrs:Omit<Partial<user>,'id'>) => Promise<Response<user>>
get:(id:user['id']) => Promise<Response<user>>
getPage:(page:number) => Promise<Response<user[]>>
}
const createResource:CreateResource = (path) => {
return {
create(attrs){}
delete(){}
update(){}
get(){}
getPage(){}
}
}
var userResource = createResource('/api/user')