高优先级知识点,需要熟练掌握。
相同点:都可定义对象类型、继承(interface extends / type &)。 不同点:
自动合并,type 不可重复声明。interface 只能定义对象 / 函数类型。interface Person {
name: string
age: number
}
interface User extends Person {
gender: number
}
// 合并后:{ name: string; age: number; gender: number }
type Point = { x: number; y: number }
type PartialPoint = Point | null
type Name = string | number
// 当你想获取一个变量的类型时,type 语句中还可以使用 typeof 获取实例的 类型进行赋值
let div = document.createElement('div')
type B = typeof div // HTMLElement
is 关键字用于类型保护
声明全局变量或类型、模块或函数等实体的类型信息
// 这是外部 JS 代码,TypeScript 无法直接识别
window.globalConfig = {
apiUrl: 'https://example.com',
debug: true
}
// 声明全局变量的类型
declare var globalConfig: {
apiUrl: string
debug: boolean
}
// 声明全局命名空间 MyLib
declare namespace MyLib {
interface User {
name: string
age: number
}
function getUser(): User
}
// 使用时 TypeScript 能识别命名空间内的类型
const user: MyLib.User = MyLib.getUser()
// 声明全局接口
declare interface Window {
// 为 window 对象扩展一个自定义属性
appVersion: string
}
// 使用时可直接访问
window.appVersion = '1.0.0' // 正确,TypeScript 认可该属性
// 新建 xxx-utils.d.ts(.d.ts 是类型声明文件)
declare module 'xxx-utils' {
export function formatDate(date: Date): string
export const version: string
}
// 在 TS 中导入使用时,会自动识别类型
import { formatDate } from 'xxx-utils'
formatDate(new Date()) // 正确,参数和返回值类型被识别
TypeScript 支持的访问修饰符有哪些?
TypeScript 中的类型别名有什么用?
类型别名可以给一个类型起一个新的名字,方便使用。
type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver
function getName(name: NameOrResolver): string {
if (typeof name === 'string') {
return name
} else {
return name()
}
}
getName('Alice') // 'Alice'
getName(() => 'Bob') // 'Bob'
TypeScript 中的联合类型有什么用?
联合类型可以表示一个值可以是多种类型中的一种。
type NameOrAge = string | number
function greet(nameOrAge: NameOrAge) {
if (typeof nameOrAge === 'string') {
console.log(`Hello, ${nameOrAge}!`)
} else {
console.log(`You are ${nameOrAge} years old.`)
}
}
greet('Alice') // Hello, Alice!
greet(25) // You are 25 years old.
TypeScript 中的交叉类型有什么用?
交叉类型可以将多个类型合并为一个类型。
type Person = { name: string }
type Employee = { salary: number }
type PersonAndEmployee = Person & Employee
function work(personAndEmployee: PersonAndEmployee) {
console.log(
`Hello, ${personAndEmployee.name}. Your salary is ${personAndEmployee.salary}.`
)
}
const person: Person = { name: 'Alice' }
const employee: Employee = { salary: 50000 }
work(person) // Hello, Alice. Your salary is undefined.
work(employee) // Hello, undefined. Your salary is 50000.
索引类型可以访问对象中元素的类型。
interface Map<T> {
[key: string]: T
}
const map: Map<number> = {
a: 1,
b: 2,
c: 3
}
function getValue(key: keyof Map<number>) {
return map[key]
}
getValue('a') // 1
getValue('b') // 2
getValue('c') // 3
条件类型可以根据条件判断来确定类型。
type IsString<T> = T extends string ? true : false
function isString(value: unknown): IsString<typeof value> {
return typeof value === 'string'
}
type IsNumber<T> = T extends number ? true : false
Typescript 中 never 和 void 的区别?
never never 表示函数根本不会执行完成(永远不会到达终点),通常用于描述 “函数会抛出异常” 或 “进入无限循环”
void 用于表示函数没有返回值。没有副作用
在 TypeScript 中,类类型接口(Class Type Interface) 是一种特殊的接口,用于约束类的结构,规定类必须实现哪些属性和方法。它类似于 “类的契约”,定义了类应该遵循的规范,包括必须包含的成员(属性、方法)及其类型。
一个类可以同时实现多个接口(用逗号分隔),需满足所有接口的约束:
interface Runable {
run(): void
}
interface Swimmable {
swim(): void
}
// 同时实现两个接口
class Duck implements Runable, Swimmable {
run(): void {
console.log('鸭子跑步')
}
swim(): void {
console.log('鸭子游泳')
}
}
联合类型(Union):值可以是多种类型之一,例如 string | number 交叉类型(Intersection):值必须同时满足多个类型,例如 Person & Employee
类型别名(Type Alias):是一种新的类型,可以给一个现有类型定义一个新的名称。
接口(Interface):是一种类型,用于描述一个对象的结构。它定义了属性、方法、事件等。
泛型(Generics)是指在定义函数、接口或类时,不预先指定具体的类型,而是使用类型参数(Type Parameter)来表示。
function identity<T>(arg: T): T {
return arg
}
identity('hello') // 'hello'
identity(25) // 25
TypeScript 支持多种类型,主要包括:
number:数字类型string:字符串类型boolean:布尔类型null:空值undefined:未定义symbol:符号类型bigint:大整数类型Object:对象类型Array:数组类型,如 number[]Tuple:元组类型,如 [string, number]Function:函数类型用于定义一组命名常量。
enum Direction {
Up,
Down,
Left,
Right
}
用于描述对象的结构。
interface Person {
name: string
age: number
}
为类型定义别名。
type Name = string
type Point = { x: number; y: number }
一个值可以是多种类型之一。
type Result = string | number
一个值同时满足多个类型。
type Employee = Person & { salary: number }
指定具体的值作为类型。
type Gender = 'male' | 'female'
定义时不指定具体类型,使用时再指定。
function identity<T>(arg: T): T {
return arg
}
表示任意类型,关闭类型检查。
let value: any = 123
value = 'abc'
表示未知类型,比 any 更安全。
let input: unknown
表示永远不会有值的类型,如抛异常或无限循环。
function error(): never {
throw new Error('error')
}
表示没有返回值的类型,常用于函数。
function log(msg: string): void {
console.log(msg)
}
TypeScript 类型系统丰富,能够满足各种场景下的类型需求,提升代码的安全性和可维护性。
TypeScript 提供了一些常用的内置工具类型,用于对已有类型进行变换和组合,提升类型系统的灵活性和表达能力。常见的工具类型如下:
将类型 T 的所有属性变为可选。
interface User {
name: string
age: number
}
type PartialUser = Partial<User>
// 等价于:{ name?: string; age?: number }
将类型 T 的所有属性变为必选。
type RequiredUser = Required<PartialUser>
// 等价于:{ name: string; age: number }
将类型 T 的所有属性变为只读。
type ReadonlyUser = Readonly<User>
// 等价于:{ readonly name: string; readonly age: number }
从类型 T 中挑选部分属性组成新类型。
type NameOnly = Pick<User, 'name'>
// 等价于:{ name: string }
从类型 T 中排除某些属性组成新类型。
type WithoutAge = Omit<User, 'age'>
// 等价于:{ name: string }
构造一个以 K 为键、T 为值的对象类型。
type UserMap = Record<string, User>
// 等价于:{ [key: string]: User }
从 T 类型中排除 U 类型的成员。
type T = 'a' | 'b' | 'c'
type T1 = Exclude<T, 'a' | 'b'>
// 结果:'c'
从 T 类型中提取 U 类型的成员。
type T2 = Extract<T, 'a' | 'b'>
// 结果:'a' | 'b'
去除类型 T 中的 null 和 undefined。
type T3 = string | number | null | undefined
type T4 = NonNullable<T3>
// 结果:string | number
获取函数类型 T 的返回值类型。
function fn(): number {
return 123
}
type FnReturn = ReturnType<typeof fn>
// 结果:number
获取函数类型 T 的参数类型组成的元组。
function greet(name: string, age: number): void {}
type GreetParams = Parameters<typeof greet>
// 结果:[string, number]
获取构造函数类型 T 的参数类型组成的元组。
class Person {
constructor(name: string, age: number) {}
}
type PersonParams = ConstructorParameters<typeof Person>
// 结果:[string, number]
获取构造函数类型 T 的实例类型。
type PersonInstance = InstanceType<typeof Person>
// 结果:Person
在 TypeScript 中,可以通过以下几种方式检查变量是否为 null 或 undefined:
let value: string | null | undefined
if (value === null) {
// value 是 null
}
if (value === undefined) {
// value 是 undefined
}
if (value == null) {
// value 可能是 null 或 undefined
}
if (typeof value === 'undefined') {
// value 是 undefined
}
const result = value ?? '默认值' // value 为 null 或 undefined 时返回 '默认值'
TypeScript 的类型保护可以自动缩小类型范围:
function printLength(str?: string | null) {
if (str != null) {
// 这里 str 类型被缩小为 string
console.log(str.length)
}
}
方法重载是指为同一个函数声明多个不同的参数类型或数量的签名,调用时根据参数自动匹配对应的签名。TypeScript 只允许一个实现体,多个类型声明。 方法重载让函数能处理多种参数类型,提升灵活性。
function add(a: number, b: number): number
function add(a: string, b: string): string
function add(a: any, b: any): any {
return a + b
}
add(1, 2) // 3
add('a', 'b') // 'ab'
装饰器(Decorator)是 TypeScript 提供的一种特殊语法,用于在类、方法、属性、参数等声明上添加元数据或扩展功能。装饰器本质上是一个函数,可以在编译时对被修饰的目标进行修改或增强。 装饰器常用于依赖注入、日志、权限校验等场景,提升代码的可扩展性和可维护性。
装饰器可以应用于以下几种:
// 类装饰器
function LogClass(target: Function) {
console.log('类被装饰:', target.name)
}
@LogClass
class Person {}
// 方法装饰器
function LogMethod(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log('方法被装饰:', propertyKey)
}
class Animal {
@LogMethod
speak() {}
}
注意:使用装饰器需要在 tsconfig.json 中开启
"experimentalDecorators": true。
Mixin 是一种复用类功能的模式,可以将多个类的功能“混入”到一个目标类中。TypeScript 支持通过函数和类型组合实现 Mixin。
type Constructor<T = {}> = new (...args: any[]) => T
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now()
}
}
class User {
name: string
constructor(name: string) {
this.name = name
}
}
const TimestampedUser = Timestamped(User)
const user = new TimestampedUser('Alice')
console.log(user.name) // 'Alice'
console.log(user.timestamp) // 时间戳
可以链式组合多个 Mixin:
function Serializable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
serialize() {
return JSON.stringify(this)
}
}
}
const MixedUser = Serializable(Timestamped(User))
const user2 = new MixedUser('Bob')
console.log(user2.serialize())
总结:TypeScript 的 Mixin 通过高阶函数和类型约束实现,可以灵活复用和组合类的功能。
优先使用 as 语法
const value = 'hello' as string
const element = document.getElementById('app') as HTMLElement
模块是 TypeScript 中用于组织和复用代码的机制。模块可以包含变量、函数、类、接口等,并通过 export 导出、import 导入,实现跨文件共享和隔离作用域。
模块让 TypeScript 项目结构更清晰,代码复用更方便。
export 导出成员import 导入其他模块成员// math.ts
export function add(a: number, b: number): number {
return a + b
}
// main.ts
import { add } from './math'
console.log(add(1, 2))
namespace 关键字声明,仅限当前文件import/export,推荐使用,支持跨文件访问TypeScript 是 JavaScript 的超集,增加了类型系统和编译时检查,推荐使用 TypeScript 的原因如下:
类型安全 编译时检测错误,减少运行时 Bug。提高代码可维护性,类型定义即文档。
代码可读性和可维护性 明确的类型定义让代码更易理解和维护,便于团队协作。
智能提示和自动补全 编辑器可根据类型信息提供更智能的代码补全和重构支持。
更强的重构能力 类型系统让重构更安全,减少因改动导致的隐性错误。
支持最新和未来的 JavaScript 特性 TypeScript 支持 ES6+ 语法,并可编译为兼容旧环境的代码。
大型项目和团队开发的最佳选择 类型约束和接口设计让大型项目结构更清晰,开发更高效。
boolean: 布尔值,true 或 falsenumber: 数字,包括整数、浮点数、二进制、八进制、十六进制string: 字符串,单引号、双引号、模板字符串bigint: 大整数,用于表示任意精度的整数symbol: 唯一标识符,用于创建对象的唯一属性键undefined: 未定义值null: 空值let isDone: boolean = false
let count: number = 42
let name: string = 'TypeScript'
let bigNumber: bigint = 123n
let sym: symbol = Symbol('id')
let u: undefined = undefined
let n: null = null
T[]: 元素类型为 T 的数组Array<T>: 泛型数组语法let numbers: number[] = [1, 2, 3]
let strings: Array<string> = ['a', 'b', 'c']
let mixed: (string | number)[] = ['hello', 42]
let tuple: [string, number] = ['hello', 42]
let optionalTuple: [string, number?] = ['hello'] // 可选元素
let restTuple: [string, ...number[]] = ['hello', 1, 2, 3] // 剩余元素
object: 非原始值类型{}: 空对象类型(可包含任何非 null/undefined 的值)let obj: object = { x: 0 }
let emptyObj: {} = { x: 0 }
interface User {
name: string
age: number
email?: string // 可选属性
readonly id: number // 只读属性
}
// 函数类型
let add: (x: number, y: number) => number = (x, y) => x + y
// 函数重载
function greet(name: string): string
function greet(age: number): string
function greet(value: string | number): string {
return typeof value === 'string' ? `Hello ${value}` : `Age: ${value}`
}
let id: string | number = '123'
let status: 'loading' | 'success' | 'error' = 'loading'
interface A {
x: number
}
interface B {
y: string
}
type C = A & B // { x: number; y: string; }
let direction: 'up' | 'down' | 'left' | 'right' = 'up'
let count: 1 | 2 | 3 = 1
enum Color {
Red,
Green,
Blue
}
enum Status {
Active = 1,
Inactive = 0
}
enum Direction {
Up = 'UP',
Down = 'DOWN'
}
interface Box<T> {
value: T
}
let stringBox: Box<string> = { value: 'hello' }
let numberBox: Box<number> = { value: 42 }
type Partial<T> = {
[P in keyof T]?: T[P]
}
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type NonNullable<T> = T extends null | undefined ? never : T
type ApiResponse<T> = T extends string ? { message: T } : { data: T }
Partial<T>: 所有属性变为可选Required<T>: 所有属性变为必需Readonly<T>: 所有属性变为只读Record<K, V>: 键类型为 K,值类型为 V 的对象Pick<T, K>: 从 T 中选择属性 KOmit<T, K>: 从 T 中排除属性 KExclude<T, U>: 从 T 中排除可赋值给 U 的类型Extract<T, U>: 从 T 中提取可赋值给 U 的类型NonNullable<T>: 排除 null 和 undefinedParameters<T>: 函数参数类型ReturnType<T>: 函数返回类型interface User {
id: number
name: string
email: string
}
type PartialUser = Partial<User> // 所有属性可选
type UserEmail = Pick<User, 'email'> // { email: string }
type UserWithoutId = Omit<User, 'id'> // { name: string; email: string }
any: 任意类型,关闭类型检查unknown: 类型安全的 any,需要类型断言或类型守卫never: 永不存在的值的类型void: 函数无返回值let anything: any = 42
let something: unknown = 'hello'
let neverValue: never = (() => {
throw new Error()
})()
function noReturn(): void {
console.log('hello')
}
let value: unknown = 'hello'
let str: string = value as string
let str2: string = <string>value // JSX 中不推荐
function isString(value: unknown): value is string {
return typeof value === 'string'
}
if (isString(value)) {
// 在此块中,value 被推断为 string
console.log(value.toUpperCase())
}
什么是类型断言?
类型断言是告诉 TypeScript 编译器某个值的具体类型的一种方式。它不会在运行时改变值,只是告诉编译器"相信我,我知道这个值的类型"。
as 语法(推荐)let value: unknown = 'Hello TypeScript'
let str: string = value as string
let num: number = (value as string).length
let value: unknown = 'Hello TypeScript'
let str: string = <string>value
let num: number = (<string>value).length
any 类型let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length
function getLength(value: string | number): number {
// 类型断言告诉编译器 value 是 string
return (value as string).length
}
let input = document.getElementById('myInput') as HTMLInputElement
input.value = 'Hello' // 现在可以安全访问 value 属性
// 或者
let input2 = <HTMLInputElement>document.getElementById('myInput')
unknown 类型let userInput: unknown = getUserInput()
let userName: string = userInput as string
// 更安全的做法是结合类型守卫
if (typeof userInput === 'string') {
let userName: string = userInput // 不需要类型断言
}
// 类型断言:编译时概念,运行时无影响
let value: unknown = '123'
let str: string = value as string // 运行时 value 仍然是 unknown
// 类型转换:运行时实际转换
let num: number = Number(value) // 运行时将字符串转换为数字
当类型断言不够直接时,可以使用双重断言:
let value: unknown = 'Hello'
// 错误:unknown 不能直接断言为 HTMLElement
// let element: HTMLElement = value as HTMLElement;
// 正确:先断言为 any,再断言为目标类型
let element: HTMLElement = value as any as HTMLElement
// 类型断言:强制告诉编译器类型
function processValue(value: unknown) {
let str = value as string // 强制断言,可能不安全
console.log(str.toUpperCase())
}
// 类型守卫:运行时检查类型
function processValueSafe(value: unknown) {
if (typeof value === 'string') {
let str = value // 类型收窄,安全
console.log(str.toUpperCase())
}
}
interface ApiResponse {
data: any
status: number
}
function handleApiResponse(response: unknown) {
const apiResponse = response as ApiResponse
if (apiResponse.status === 200) {
console.log(apiResponse.data)
}
}
function handleClick(event: Event) {
const target = event.target as HTMLButtonElement
target.disabled = true
}
// 假设第三方库没有类型定义
declare const someLibrary: any
const libraryInstance = someLibrary as {
init: () => void
destroy: () => void
}
// 不推荐:直接断言
function badExample(value: unknown) {
return (value as string).toUpperCase()
}
// 推荐:类型守卫
function goodExample(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase()
}
throw new Error('Expected string')
}
any// 不推荐
let data: any = getData()
let result = (data as any).someProperty
// 推荐:定义具体类型
interface DataType {
someProperty: string
}
let data: unknown = getData()
let result = (data as DataType).someProperty
function isApiResponse(value: unknown): value is ApiResponse {
return (
typeof value === 'object' &&
value !== null &&
'data' in value &&
'status' in value
)
}
function processResponse(response: unknown) {
if (isApiResponse(response)) {
// 类型收窄,无需断言
console.log(response.data)
}
}
Mixin 是一种设计模式,允许将多个类的功能组合到一个类中,实现代码复用和组合式继承。在 TypeScript 中,Mixin 通过交叉类型和类表达式来实现。