【进阶】TS 中的 类型断言 和 泛型

类型断言

作用 : 手动指定值的具体类型 ( 缩写值的范围 )

应用场景 1

获取 DOM 元素的时候指定具体元素

示例 :

const box = document.getElementById('img')
console.log(box.src) // ts报错

错误内容 :
在这里插入图片描述

解析 :
上述语法在 js 中可以直接使用, 但是 TS 中就不行啦, 由于类型不明确会报错

  • 返回值的类型是 HTMLElement,只有标签公共的属性,不包含 imgsrc 属性

解决方法 : 使用类型断言明确具体类型

语法 : 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…

<think>嗯,用户想了解TypeScript类型使用指南常见问题。首先,我需要整理一下已有的引用资料。引用[1]提到了基本数据类型类型注解的重要性,引用[2]讲的是接口对象类型,引用[3]涉及类型推断,引用[4]讨论了条件类型中的联合类型处理。 用户的需求可能包括基础类型的使用、高级类型如联合类型类型别名、接口,以及类型推断类型守卫这些常见问题。需要分步骤解释,先讲基础,再深入高级特性,然后解决常见问题。 首先,类型注解是TypeScript的核心,应该从基本类型开始,比如string、number、boolean等,然后介绍数组元组。接着是联合类型类型别名,这部分引用[4]中的联合类型处理需要注意。 然后是接口对象类型,引用[2]提到接口的作用,需要详细说明如何定义接口可选属性。类型推断部分可以引用[3],区分letconst的不同推断结果。 高级类型方面,、条件类型类型守卫都需要覆盖,引用[4]中的条件类型处理联合类型的情况要举例说明。常见问题部分,类型不匹配、联合类型操作类型断言的风险都是重点,需要结合引用中的内容。 最后,生成相关问题时要围绕用户可能遇到的进阶问题,比如类型兼容性、高级工具类型的使用,以及性能优化等。确保引用标记正确,每个引用对应正确的知识点。整个回答结构要清晰,逐步引导用户从基础到高级,同时解决他们可能遇到的常见问题。</think>### TypeScript 类型使用指南与常见问题 #### 一、TypeScript 类型核心概念 1. **基本类型注解** TypeScript 提供了与 JavaScript 相同的基础类型(如 `string`、`number`、`boolean`),并通过类型注解增强代码安全性。例如: ```typescript let name: string = "Alice"; const age: number = 30; ``` 类型注解可显式定义变量类型,减少运行时错误[^1]。 2. **数组与元组** - 数组类型:`number[]` 或 `Array<number>` - 元组类型:固定长度类型的数组,如 `[string, number]` ```typescript let tuple: [string, number] = ["TypeScript", 2023]; ``` 3. **联合类型类型别名** - 联合类型通过 `|` 组合多种类型:`string | number` - 类型别名通过 `type` 定义复杂类型: ```typescript type ID = string | number; ``` #### 二、对象类型与接口 1. **接口定义** 接口用于描述对象结构,支持可选属性只读属性[^2]: ```typescript interface User { readonly id: number; name: string; email?: string; // 可选属性 } ``` 2. **类型推断规则** - `let` 声明的变量推断为基本类型(如 `string`) - `const` 声明的常量推断为字面量类型(如 `"JavaScript"`)[^3] ```typescript let s1 = "JavaScript"; // 类型: string const s2 = "JavaScript"; // 类型: "JavaScript" ``` #### 三、高级类型技巧 1. **与条件类型** - 支持类型参数化: ```typescript function identity<T>(arg: T): T { return arg; } ``` - 条件类型根据条件返回不同类型,联合类型会逐成员处理[^4]: ```typescript type IsString<T> = T extends string ? true : false; ``` 2. **类型守卫与类型断言** - 类型守卫:通过 `typeof`、`instanceof` 或自定义函数缩小类型范围 - 类型断言:显式指定类型(需谨慎使用): ```typescript let value: any = "hello"; let length: number = (value as string).length; ``` #### 四、常见问题与解决方案 1. **类型不匹配错误** - **场景**:将 `string` 赋值给 `number` 类型变量 - **解决**:检查赋值逻辑或使用类型转换 2. **联合类型操作限制** - **场景**:`string | number` 类型变量无法直接调用 `toUpperCase()` - **解决**:使用类型守卫缩小范围: ```typescript if (typeof value === "string") { value.toUpperCase(); } ``` 3. **类型断言的风险** - **场景**:错误使用 `as` 导致运行时异常 - **建议**:优先使用类型守卫或重构代码逻辑
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疆子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值