1、类型注解和类型推断
- 在typescript中,类型注解(type annotation)和类型推断(type inference)是用来确定变量、函数和表达式类型得俩种方法。
2、类型注解
- 类型注解是明确的给变量、函数参数、函数返回值或表达式指定类型的方式。使用
“ : ”后跟类目名称来注解。let age: number = 30; function greet(name: string): string { return 'heelo'+name } // 这个例子中的,age、name、greet()的返回值,都用了类型注解。
3、类型推断
- 类型推断是
TypeScript的一种功能,根据变量的赋值表达式自动推断出变量的类型。ts会根据上下文和赋值的类型去推断出变量的类型,而无需再显式注解。
4、泛型
- 当数据类型不是固定的时候,可以使用泛型将数据类型变成动态的。也就是先不定义数据的一个具体类型,当使用的时候再指定。
5、函数里的泛型
- 在函数名后面跟上
<T>,其中T就是动态的类型,形参value的类型也就是T,返回的也是一个T类型的数组。当调用函数的时候,在函数名后面传入需要定义的数据类型<string>,此时函数内部所有T都会被替换成string。// 泛型可以定义多个。 function getData<T, K>(value: T, value: K): Array<T | K> { return [value1, value2] } getData(1, 2) getData<string, number>("3", 4) getData(true, null)
6、泛型接口
- 在
interface或者type中也是可以使用泛型,用法一样。interface ResData<T> { code: number, msg: string, data: T } const response: ResData<boolean> = { code: 1, msg: 'success', data: false } type Data<K> = number | string | K const data: Data<null> = null
7、泛型变量
- 看别人的
ts代码会看到很多泛型T、E、V...之类的变量,为什么有的地方是T,有的地方是V。TypeScript在这方面没有强制性要求,都是开发者们为了方便阅读理解,默认形成得一套规范。T:表示一般的泛型类型参数。K:表示对象中的键类型。V:表示对象中的值类型。E:表示数组或元组中的元素类型。R:表示函数的返回类型。S,U,V,...:表示额外的泛型类型参数。
8、泛型约束
- 在
函数内部使用泛型时,还不知道是什么类型,也就不能使用、操作数据的属性或者方法。function test<T>(name: T): void { console.log('name length', name.length) // 类型 T 不存在属性 length } - 此时就可以使用
泛型约束,对使用的泛型数据进行约束,约束为具有length属性的类型,如string、Array。 - 语法:
<T extends 类型>- 这里定义一个 Len 接口类型里面的属性有个length,而赋值给它的类型,要是没有length属性,就会报错。
interface Len { length: number } function test<T extends Len>(name: T): void { console.log('name length', name.length) } test('1') test([1,2,3]) test(2) // 类型 number 的参数不能赋值给类型 Len 的参数 test(false) // 类型 boolean 的参数不能赋值给类型Len 的参数
- 这里定义一个 Len 接口类型里面的属性有个length,而赋值给它的类型,要是没有length属性,就会报错。
9、泛型和any的差别
any 类型:any类型允许你将任何类型分配给它,而TypeScript将不会进行任何类型检查。这样的灵活性也带来了潜在的问题,因为你可能会意外的使用错误的类型,而在编译时不会得到任何警告。let myVar: any = 10; myVar = 'Hello'; // any 失去类型检查,不管插入什么值都可以。
泛型:- 泛型是一种用于创建可重用代码的工具,它允许你编写与类型无关的代码。通过使用泛型,你可以编写一些函数或类,它们可以使用于多种类型而不失去类型安全性。
- 泛型允许你在使用函数或类的时候指定类型,从而在编译时进行类型检查。
// 使用泛型 function identity<T>(arg: T): T { return arg } let result: stirng = identity("hello") // 此时既有灵活性,又保证了类型的安全。
总结:any提供了更大的灵活性,但也失去了类型的安全性。而泛型则是在保持类型安全的同时提供了一定的灵活性。- 在上面案例中,函数
identity接收一个泛型参数arg,并声明一个局部变量result,其类型也是T。这确保了result的类型与传入的参数类型相同。 泛型T在函数内部的使用是一种类型一致性的保证,让函数可以灵活地处理不同类型的输入数据,同时保持类型安全。
10、type和interface的区别?
1、共同点:
-
基本用法相似:
- 都可以用来定义对象的形状。
- 都可以用来描述函数、数组等类型。
type PointType = { x: number; y: number; }; interface PointInterface { x: number; y: number; } -
都可以用于描述函数类型:
type AddType = (a: number, b: number) => number; interface AddInterface { (a: number, b: number): number; }
2、区别:
-
扩展(扩展性):
interface可以通过extends扩展,可以继承多个接口。type可以通过交叉类型(&)进行组合,但不能继承。
interface Animal { name: string; } interface Dog extends Animal { breed: string; } type AnimalType = { name: string; }; type DogType = AnimalType & { breed: string; }; -
声明合并:
interface可以进行声明合并,即多个同名接口会自动合并为一个接口。type不支持声明合并,如果定义多个同名 type 会报错。
interface Person { name: string; } interface Person { age: number; } // 合并后 // interface Person { // name: string; // age: number; // } type PersonType = { name: string; }; type PersonType = { age: number; }; // Error: Duplicate identifier 'PersonType'. -
使用范围:
type可以声明任何类型,包括基本类型、联合类型、交叉类型、元组等。interface主要用于声明对象类型。
type Name = string; type Union = string | number; type Tuple = [number, string]; -
可选属性和只读属性:
type和interface都支持可选属性(?)和只读属性(readonly)。
type PointType = { readonly x: number; y?: number; }; interface PointInterface { readonly x: number; y?: number; } -
类型别名的高级功能:
type可以使用映射类型、条件类型、模板字面量类型等高级类型功能。
type Keys = "a" | "b" | "c"; type Flags = { [K in Keys]: boolean }; type ConditionalType<T> = T extends string ? string : number; type TemplateLiteralType = `${Keys}_suffix`;
3、选择建议
-
使用
interface:- 如果需要使用
声明合并特性。 - 如果需要
扩展或实现一个接口。 - 如果编写
库或框架,建议使用接口,因为它的扩展性更强。
- 如果需要使用
-
使用
type:- 如果需要使用高级类型(如
联合类型、交叉类型、映射类型、条件类型)。 - 如果需要定义基本类型
别名、联合类型、元组类型等。 - 如果
不需要使用接口的扩展和声明合并特性。
- 如果需要使用高级类型(如
11、联合类型、交叉类型、映射类型、条件类型、模板字面量类型、元组类型是什么?
1、简述:
- 联合类型: 定义
一个变量可以是多种类型之一。 - 交叉类型: 合并
多个类型为一个新类型,包含所有原类型的属性。 - 映射类型: 基于
已有类型创建一个新类型,常用于添加修饰符。 - 条件类型: 根据
条件表达式选择类型,实现类型的动态选择和推断。 - 模板字面量类型: 用于生成
基于字符串模板的类型。 - 元组类型: 表示
已知元素数量和类型的数组,每个元素可以有不同的类型。
2、详解:
-
联合类型
- 联合类型表示一个变量可以是几种类型之一。使用竖线 (
|) 分隔各个类型。
type StringOrNumber = string | number; let value: StringOrNumber; value = "hello"; // valid value = 123; // valid value = true; // error - 联合类型表示一个变量可以是几种类型之一。使用竖线 (
-
交叉类型
- 交叉类型将多个类型合并为一个类型。使用与符号 (
&) 连接各个类型。结果类型包含所有合并类型的所有属性。
type Person = { name: string; }; type Employee = { id: number; }; type PersonEmployee = Person & Employee; let employee: PersonEmployee = { name: "John", id: 123 }; - 交叉类型将多个类型合并为一个类型。使用与符号 (
-
映射类型
- 映射类型根据一个
已有的类型创建一个新的类型。可以使用in关键字遍历一个联合类型,并对每个成员应用一个转换。
type Keys = "a" | "b" | "c"; type Flags = { [K in Keys]: boolean }; // Resulting type: // type Flags = { // a: boolean; // b: boolean; // c: boolean; // }- 映射类型常用于创建带有特定修饰符(如可选、只读)的类型。
type Readonly<T> = { readonly [P in keyof T]: T[P] }; type Partial<T> = { [P in keyof T]?: T[P] }; type User = { id: number; name: string; }; type ReadonlyUser = Readonly<User>; type PartialUser = Partial<User>; - 映射类型根据一个
-
条件类型
- 条件类型根据一个条件表达式在类型之间进行选择。
- 语法是
T extends U ? X : Y。
type IsString<T> = T extends string ? "yes" : "no"; type A = IsString<string>; // "yes" type B = IsString<number>; // "no"- 条件类型还可以用于类型推断和复杂类型转换。
type Exclude<T, U> = T extends U ? never : T; type NonNullable<T> = T extends null | undefined ? never : T; type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c" type T1 = NonNullable<string | number | undefined>; // string | number -
模板字面量类型
- 模板字面量类型允许在类型中使用字符串模板字面量。它可以用于构建复杂的字符串类型。
- 模板字面量类型可以用于字符串操作,如拼接、条件匹配等。
type Color = "red" | "green" | "blue"; type ColorVariant = `${Color}-light` | `${Color}-dark`; // Resulting type: // type ColorVariant = "red-light" | "green-light" | "blue-light" | "red-dark" | "green-dark" | "blue-dark" -
元组类型
- 元组类型表示一个已知元素数量和类型的数组,每个元素可以有不同的类型。
let tuple: [number, string, boolean]; tuple = [42, "hello", true]; // valid tuple = ["hello", 42, true]; // error- 元组类型可以通过扩展来支持可选元素和剩余元素。
// [42] or [42, "hello"] type TupleWithOptional = [number, string?]; // [42] or [42, "a", "b", "c"] type TupleWithRest = [number, ...string[]];