一、TS类型声明
1.在.ts文件中定义变量a,提示无法重新声明块范围变量“a”。
原因分析:TS误以为这些文件将在未来的某个时间合并,所以抛出了变量“重复”的错误。
解决方法:在文件末尾加上export {}即可。(项目中已经有了exports模块化工具后才能使用)
let a: number = 12;
export {}
2.如果变量的声明和赋值是同时进行的,TS可以对变量进行
let b = 13; // 声明变量b 并赋值为13
b = true; // 报错,因为b的类型是number,不能赋值为boolean
3.如果只声明变量不赋值,且没有指定变量的类型,TS会自动判断该变量的类型为
// 变量只声明不赋值,且没有指定变量的类型时,TS自动推断变量的类型为any
let c;
c = 1;
c = "hello";
c = true;
4.给函数的
和
声明类型
function sum(a: number, b: number):number {
return a + b;
}
二、TS基本类型
| 类型 | 例子 | 描述 |
|---|---|---|
| number | 1,2,3 | 任意数字 |
| string | 'hello' | 任意字符串 |
| boolean | true,false | 布尔值true或false |
| 字面量 | 其本身 | 限制变量的值就是该字面量的值 |
| any | * | 任意类型 |
| unknown | * | 类型安全的any |
| void | 空值(undefined) | 没有值(或undefined) |
| never | 没有值 | 不能是任何值 |
| object | {name:'孙悟空'} | 任意的JS对象 |
| array | [1,2,3] | 任意JS数组 |
| tuple | [4,5] | 元组,TS新增类型,固定长度的数组 |
| enum | enum{A,B} | 枚举,TS新增类型 |
1.字面量类型
字面量类型的变量,只能被赋值为指定的值。
// 字面量类型的变量,只能被赋值为指定的值
let d: "male" | "female";
d = "male";
d = "female";
TS类型声明时,
代表
。
// 声明变量类型时,可以用 | 连接多个类型,表示该变量可以是多个类型中的一个
let e: string | number;
e = "hello";
e = 123;
2.any和unknown的区别。
(1)可以把任意类型的值赋值给any或unknown类型的变量。
// any表示任意类型
let c: any;
c = 1;
c = "hello";
c = true;
// unknown 表示未知类型的值
let f: unknown;
f = 10;
f = "hello";
f = true;
(2)any类型的变量可以直接赋值给其他任意类型的变量;unknown类型的变量则不能直接赋值给其他类型的变量,除非对unknown类型的变量做 或
。
类型断言语法格式:
或者
let g: string;
g = c; // 不报错,因为c的类型是any
g = f; // 报错,不能将类型“unknown”分配给类型“string”
g = f as string; // 类型断言,告诉编译器f是string类型
g = <string>f; // 类型断言,告诉编译器f是string类型
// 类型判断
if (typeof f === "string") {
g = f;
}
3.void类型
void表示没有任何类型,一般用于 函数没有返回值 或者 返回undefined。
// void 表示没有任何类型,一般用于函数没有返回值或者返回undefined
function sum(x: number, y: number): void {
// return undefined;
}
没有声明函数返回值的类型且函数没有返回值时,函数返回值的类型默认为隐式的void。
4.never类型
never表示不能是任何值,永远不会有返回结果。例如函数内抛出错误。
function sum2(): never {
throw new Error("报错了!")
}
5.object类型
{属性名:类型,属性名?:类型,[任意名称:类型]:类型}
- 不加
表示属性是必须的
-
表示属性是可选的
- [propName: string]:any 表示该对象中可以添加任意个属性,属性名是string类型,属性值是any类型
// object 表示一个js对象,但这种写法不太常用
let h: object;
h = {};
/*
常用的写法:{属性名:类型,属性名?:类型,[任意名称:类型]:类型}。
? 表示属性是可选的;不加?表示属性是必须的
[propName: string]:any 表示该对象中可以添加任意个属性,属性名是string类型,属性值是any类型
*/
let i: { x: number, y?: string, [propName: string]: any };
i = { x: 1, y: "hello", name: "张三", age: 18 }; // x是必须有的,y可有可无,name和age也是可有可无
// Function 表示一个函数,但这种写法不太常用
let j: Function;
j = function () { };
// 常用的写法:(参数名:类型) => 返回值类型
let k: (a: number, b: string) => number;
k = function (a, b):number { return a + b.length; };
6.array类型
或者
/*
* 数组类型的声明:类型[] 或 Array<类型>
*/
let l: number[]; // 数组中的元素是number类型
l = [1, 2, 3];
let m: Array<string>; // 数组中的元素是string类型
m = ["a", "b", "c"];
7.tuple类型
元组就是固定长度的数组。
// 元组就是固定长度的数组
let n: [string, number]; // 元组类型,表示一个元组中只能有两个元素,第一个元素是string类型,第二个元素是number类型
n = ["hello", 123];
8.enum类型
或者
// 枚举类型
enum Gender { Male, Female }; // 定义枚举类型Gender,表示性别,有男和女两种
let o = {name: "张三", gender: Gender.Male}; // 定义对象o,name是字符串,gender是Gender枚举类型
console.log(o.gender === Gender.Male); // true
9.类型的别名
type 类型别名 = 类型
// 类型的别名: type 类型别名 = 类型
type myType = number[]; // myType等价于number类型的数组
let z: myType = [1, 2, 3]; // z的数据类型是myType,一个number类型的数组
三、接口
1.定义类结构
(1)用interface 关键字来定义接口;用 implements 关键字来实现接口。
(2)接口可以用来定义 类的结构,即一个类中应该有哪些属性和方法。
(3)接口中的 属性 和 方法 都是 抽象的(即不能有实际的值),抽象的属性和方法必须被实现(重写)。
// interface关键字来定义接口
interface myInterface {
name: string;
sayHello(): void;
}
// implements关键字来实现接口
class MyClass implements myInterface {
name: string;
constructor(name: string) {
this.name = name; // 实现抽象属性
}
// 实现抽象方法
sayHello(): void {
console.log("你好!");
}
}
2.类型声明
(1)接口也可以当做类型声明来使用。
(2)不同的是,接口可以 重复声明,而类型声明不可以。
// A和B是等价的
interface A {
name: string;
}
interface A {
age: number;
}
type B = {
name: string;
age: number;
}
四、泛型
1. 什么是泛型
(1)在定义 函数、接口 或 类 时,不预先指定具体的类型,而在 使用时 再指定类型的一种特性。
(2)使用 any 会跳过类型检查,且会丢失参数和返回值之间的类型约束关系。
2. 泛型函数
(1)重点在于用 function 关键字 定义函数 时,怎么使用泛型。
(2)基础用法
// 泛型函数,T和K是类型变量,可以用任意字母表示
function fn<T,K>(a: T, b: K): T {
console.log(b);
return a;
}
fn<number,string>(10, "hello"); // 指定T是number类型,K是string类型
fn(10, "hello"); // 不指定类型,TS自动推断T是number类型,K是string类型
(3)实际用法——用function关键字声明函数组件
① 用 function 定义了一个函数组件,用到了泛型T,并用extends关键字对T进行了 泛型约束。
② 入参的类型是 泛型接口 类型,名为 TableProps<T>。
③ 返回值是 JSX.Element类型。
import React from 'react';
// 泛型接口定义组件 Props
interface TableProps<T> {
data: T[];
columns: Array<{
key: keyof T;
title: string;
render?: (value: T[keyof T], record: T) => React.ReactNode;
}>;
}
// 泛型函数组件
function GenericTable<T extends { id: string }>({
data,
columns
}: TableProps<T>): JSX.Element {
return <div></div>
}
3. 泛型接口
(1)本质就是 接口,而这个接口中用到了 泛型,目的是指定 变量 的类型。
(2) 基础用法
// 泛型接口
interface ApiResponse<T> {
code: number;
message: string;
data: T; // data 的类型由使用接口时指定
}
// 用户信息的接口响应数据
let userResponse: ApiResponse<{ name: string; age: number }> = {
code: 200,
message: "Success",
data: { name: "Alice", age: 30 } // data 必须是 { name: string; age: number }
};
(2) 实际用法——用箭头函数声明函数组件
① 这里用 箭头函数 定义了一个 函数组件,并用 变量 进行存接收存储。
② React.FC<UserProps>:这是对组件整体的类型注解,而组件存储到了变量中,所以实际修饰的是,变量的类型就是
。
③ React.FC 是 React已经定义好的 内置泛型接口, 表示 变量的类型 是 React 函数组件。
④ <UserProps> 是泛型参数,React.FC这个泛型变量接受到泛型参数后,在内部用它指定了组件的 props 类型。
// 1. 定义 Props 接口
interface UserProps {
name: string;
age: number;
isActive?: boolean; // 可选属性
}
// 2. 组件类型注解
const UserCard: React.FC<UserProps> = (props) => {
// 此时 props 的类型已经被推断为 UserProps
// 包含 name: string, age: number, isActive?: boolean
// 以及 React.FC 隐含的 children?: React.ReactNode
}
4. 泛型类
// 泛型类
class B<T> {
name: T;
constructor(name: T) {
this.name = name;
}
}
let b1 = new B<string>("张三"); // 指定T是string类型
5. 泛型约束
如果不希望泛型可以是任何类型,而是希望它 至少满足某些条件。这时就需要使用 extends 关键字来实现泛型约束。
// 定义一个接口,表示必须有length属性
interface Lengthwise {
length: number;
}
// 定义一个泛型函数,T必须是Lengthwise类型或其子类型
function fn1<T extends Lengthwise>(a: T): number {
return a.length; // 现在可以安全地访问length属性
}
6. 泛型默认值
...
五、常用的内置类型工具
1. Omit<T, K> 泛型工具类型
(1)用于从 类型T 中 排除 指定的 属性集合K,生成一个 新的类型。
type myNewType = Omit<T, K>
type myNewType= Omit<myOldType,"name" | "age"> // myNewType中包含myOldType中,除了name和age之外的所有类型。
2. Partial<T> 泛型工具类型
(1)Partial 将类型 T 的所有属性都转换为 可选属性(即添加 ? 修饰符)。
(2)Partial 只会影响对象的 直接属性,不会递归地将嵌套对象的属性变为可选。
type myNewType = Partial<T>
/*
* 1.定义一个变量partialUser,变量类型是用 Partial修饰后的 myOldType类型。
* 2.变量 partialUser可以只包含 myOldType 类型的部分属性。
*/
let partialUser: Partial<myOldType> = { name: '张三', age: 25 };
3. 常用类型工具
(1)Partial<T>:将类型 T 的所有属性设置为可选的。
(2)Required<T>:将类型 T 的所有属性设置为必需的。
(3)Readonly<T>:将类型 T 的所有属性设置为只读的。
(4)Record<K, T>:构造一个类型,其属性名的类型为 K,属性值的类型为 T。
(5)Pick<T, K>:从类型 T 中选取一组属性 K 来构造类型。
(6)Omit<T, K>:从类型 T 中排除一组属性 K 来构造类型。
(7)Exclude<T, U>:从类型 T 中排除那些可以赋值给 U 的类型。
(8)Extract<T, U>:从类型 T 中提取那些可以赋值给 U 的类型。
(9)NonNullable<T>:从 T 中排除 null 和 undefined。
(10)Parameters<T>:由函数类型 T 的参数类型组成的元组类型。
(11)ReturnType<T>:函数类型 T 的返回值类型。
(12)InstanceType<T>:构造函数类型 T 的实例类型。
(13)ThisParameterType<T>:提取函数类型 T 的 this 参数类型,如果没有则返回 unknown。
(14)OmitThisParameter<T>:从函数类型 T 中移除 this 参数。
(15)ThisType<T>:用于标记上下文 this 类型的工具类型。
interface User {
name: string;
age: number;
email?: string;
}
// (1)Partial: 所有属性变为可选
type PartialUser = Partial<User>;
// 等价于 { name?: string; age?: number; email?: string; }
// (2)Required: 所有属性变为必选
type RequiredUser = Required<User>;
// 等价于 { name: string; age: number; email: string; }
// (3)Readonly: 所有属性变为只读
type ReadonlyUser = Readonly<User>;
// 等价于 { readonly name: string; readonly age: number; readonly email?: string; }
// (4)Record: 构建一个类型,其键为字符串,值为User
type UserRecord = Record<string, User>;
// (5)Pick: 选取User中的'name'和'age'
type UserNameAge = Pick<User, 'name' | 'age'>;
// 等价于 { name: string; age: number; }
// (6)Omit: 排除User中的'age'
type UserWithoutAge = Omit<User, 'age'>;
// 等价于 { name: string; email?: string; }
// (7)Exclude: 从联合类型中排除某些类型
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// (8)Extract: 从联合类型中提取某些类型
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// (9)NonNullable: 排除null和undefined
type T2 = NonNullable<string | number | null | undefined>; // string | number
// (10)Parameters: 获取函数的参数类型
declare function f1(arg: { a: number; b: string }): void;
type T3 = Parameters<typeof f1>; // [{ a: number; b: string }]
// (11)ReturnType: 获取函数的返回类型
type T4 = ReturnType<() => string>; // string
// (12)InstanceType: 获取构造函数的实例类型
class C {
x = 0;
y = 0;
}
type T5 = InstanceType<typeof C>; // C
// (13)ThisParameterType: 获取函数的this参数类型
function toHex(this: number) {
return this.toString(16);
}
type T6 = ThisParameterType<typeof toHex>; // number
// (14)OmitThisParameter: 移除this参数
type T7 = OmitThisParameter<typeof toHex>; // () => string
// (15)ThisType: 用于标记对象中方法的this类型
interface MyObject {
text: string;
highlight: () => void;
}
type MyObjectThis = ThisType<{ enable: () => void }>;
// 通常与具体对象定义一起使用,例如在选项对象中指定this类型
六、Typescript基本使用
(1)安装,npm install -g typescript。
(2)配置typescript.config.ts文件。(不配置也可以直接进行第三步)
(3)命令行里输入 tsc 文件名.ts进行编译;或者命令行输入tsc -w,当前ts文件变更时会自动编译到js文件。
(4)在项目里使用时,可以结合webpack或者vite等打包工具,进一步配置webpack或者vite。