类型断言
作用 : 手动指定值的具体类型 ( 缩写值的范围 )
应用场景 1
获取 DOM 元素的时候指定具体元素
示例 :
const box = document.getElementById('img')
console.log(box.src) // ts报错
错误内容 :
解析 :
上述语法在 js
中可以直接使用, 但是 TS
中就不行啦, 由于类型不明确会报错
- 返回值的类型是
HTMLElement
,只有标签公共的属性,不包含img
的src
属性
解决方法 : 使用类型断言明确具体类型
语法 : let 变量 = 值 as 类型
as
关键字实现类型断言- 关键字
as
后面的类型是一个具体的类型
实例 :
const box = document.getElementById('img') as HTMLImageElement
- 指定了具体类型后 , 就可以使用
box.src
了
技巧: 获取 DOM 元素类型的方法
- 在浏览器控制台,通过
__proto__
获取 DOM 元素的类型; document.createElement('a')
,然后看代码提示、
应用场景 2
知道获取数据的类型, 但是不知道具体的数据时 , 使用类型断言后就会有提示
type User = {
name: string,
age: number
}
const u1 = {} as User
console.log(u1.name) // 这里就会有提示
非空断言
作用 :
明确地知道对象的属性一定不会为空,可以使用非空断言 !
应用场景 : TS 中 使用 useRef()
JS 中的语法直接使用会报错
错误解析 :
-
useRef
类型说明 :useRef
的泛型参数用于指定current属性的值的类型
-
传入参数
null
时 ,current
属性的值类型也被指定为null
解决方法 :
- 指定
useRef
的泛型参数类型为HTMLInputElement
(已知类型) - 使用非空断言
!
指定current
属性的值不为空
function App() {
const inputRef = useRef<HTMLInputElement>(null)
return (
<div className="App">
<input type="text" ref={inputRef} />
<button onClick={() => {
console.log(inputRef.current!.value)
}}>点击</button>
</div>)
}
typeof
TS 中的 typeof 操作符
和 js 中的一样都是用来获取变量或属性类型
使用方法 :
获取类型 : type 变量 = typeof 常量
使用 : function(参数:变量){参数.常量的方法}
应用场景
获取到一个复杂的对象,我们把它当做实参传给一个函数fn,但是没有办法在使用形参
时获取提示信息
typeof 解决
const res = { name: '小花', city: '武汉', skills: ['js', 'css'] }
type Stu = typeof res
function fn(obj:Stu) {
// 这里写 obj. 就会有提示
obj.skills
}
fn(res)
注意 : typeof 只能用来查询变量或属性的类型,无法查询其他形式的类型(比如,函数调用的类型)
keyof
获取对象类型的属性名 , 得到新的类型
type 类型 = keyof 类型
- 实例
type Point = { x: number; y: number }
type P = keyof Point
- 实例
type 类型 = keyof 对象常量
type T = keyof {a:1,b:2}
使用方法
获取类型的属性名 :
type T = keyof {a:1,b:2}
==type T = 'a'|'b'
使用 : let 变量:T = 'a'
- 变量的取值只能是 ‘a’或者’b’
泛型
什么是泛型
顾名思义就是多个类型 , TS 中指使用变量保存要使用的类型
泛型本质
泛型本质是参数化类型 , 就是所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和函数的创建中,分别成为泛型类,泛型接口、泛型函数
泛型函数
什么是泛型函数 :
这个函数的参数类型或者返回值的类型是可变的
定义泛型函数 :
function 函数名<类型参数> (形参:类型参数):类型参数{}
- 在函数名称的后面写
<>
(尖括号),尖括号中添加类型变量 - 类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)
使用泛型函数
- 方法1:
const 返回值 = 函数名(实参)
类型推论实参的类型,传递给类型参数 - 方法2 :
const 返回值= 函数名<类型实参>(实参)
上述的非空断言就使用了此种方法- 此时的
<>
中的就是具体的类型
- 此时的
示例
// 泛型函数
function useState<T>(val: T | (() => T)): [T | (() => T), (newvalue: T) => void]{
return [val,(newvalue)=>{val=newvalue}]
}
const [str,setStr] = useState('1')
const [num,setNum] = useState<number>(1)
const [fn, setFn] = useState<number>(() => 1)
泛型推论
泛型推论就是, 利用类型推论简化函数调用
- 在调用泛型函数时,可以省略
<类型>
来简化泛型函数的调用 - 此时,TS 内部采用一种类型参数推断的机制,来根据传入的实参自动推断出类型变量 Type
泛型对象
应用场景 :
函数中传入对象和该对象的属性,要求得到给对象的值
// 泛型对象
function getProp<T, K extends keyof T>(arr: T, key: K) {
return arr[key]
}
let obj ={name:'ikun',age:18,hobby:'学习'}
getProp(obj,'hobby')
解析 :
上述约定后, 后续调用时会有提示
泛型的问题
太灵活了
- 默认情况下,泛型函数的类型变量 可以代表多个类型,这导致在泛型函数内部无法访问任何属性
function fn<T>(value: T): T {
// 这里value. 不会有提示
console.log(value.length)// 这里会报错
return value
}
fn('a')
解决泛型太灵活的方法
为泛型添加约束来收缩类型
(缩窄类型取值范围)
泛型约束
泛型约束主要有以下两种方式:
- 指定更加具体的类型
- 添加约束
指定更加具体的类型
指定更具体的类型缩小范围
function fn<T>(value: T[]): T[] {
console.log(value.length) // 这里就可以获得提示
return value
}
T[]
数组一定会有length
属性
添加约束(推荐使用)
对已有类型做继承,已有类型中指定了 length
属性
interface ILength { length: number }
function fn1<T extends ILength>(value: T): T {
// 这里value. 不会有提示
console.log(value.length)
return value
}
泛型接口
在接口中使用泛型来使用,以增加其灵活性,增强其复用性
语法 :
interface 接口名<类型变量1,类型变量2...> {
属性名1:类型1,
属性名2:类型2,
属性名3:类型3
}
注意 :
- 在接口名称的后面添加
<类型变量>
,那么,这个接口就变成了泛型接口。 - 接口的类型变量,对接口中所有其他成员可见,也就是接口中所有成员都可以使用类型变量。
- 使用泛型接口时,需要显式指定具体的类型
实例:
interface MyArray {
length: number,
push(n: number):void,
pop():number,
reverse():number[]
}
let obj: MyArray = {
id(value) { return value },
ids() { return [1, 3, 5] }
}
数组在ts中就是一个泛型接口
泛型工具类型
Partial
把已有的类型属性,变成一个新类型的可选属性
语法 : type 新类型 = partial<老类型>
Readonly
把已有类型全部转换为只读类型
语法和上述一直
定义对象使其内在属性不能修改
Pick
从已有类型中选一些属性来构造新的类型
语法 : type 新类型 = partial<老类型,属性|属性2>
详见代码
代码地址: github.com/jiang-zi/TS…