
TypeScript 是 Javascript 的一个超集,并且向这个语言添加了可选的静态类型和基于类的面向对象编程
// Boolean
let isFirst: boolean = true;
// Number
let count: number = 10;
// String
let hellomsg: string = "Hello World";
// Array
let myArray: Array<number> = [1, 2, 3];
let myArray2: number[] = [1, 2, 3];
/// Enum
// 数字枚举
enum Direction {
default,
horizontal,
vertical,
}
let dir: Direction = Direction.default; // dir = 0
enum Direction2 {
default = 2,
horizontal,
vertical,
}
let dir2: Direction = Direction.horizontal; // dir2 = 3
// 字符串枚举
enum Direction3 {
default = "default",
horizontal = "horizontal",
vertical = "vertical",
}
let dir3: Direction3 = Direction3.default; // dir3 = 'default'
// 异构枚举
enum Anisotropy {
A,
B,
C = "C",
D = "D",
E = 8,
F,
}
// Any
let a: any = 10;
a = "hello";
a = false;
// Unknown
let value: unknown;
value = true;
value = 42;
value = "hello";
value = [];
value = {};
value = Math.random();
value = new Date();
value = null;
value = undefined;
value = new TypeError();
value = Symbol("type");
let value1: unknown = value;
let value2: any = value;
let value3: string = value;
// Tuple 元组
let tupleType: [number, string];
tupleType = [1, "hello"];
// Void
function warnTest(): void {
console.log("this is a no return function");
}
// Never
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
// 假如后来有一天修改了 Foo 的类型
// 使用 never 避免出现新增了联合类型没有对应的实现,目的就是写出类型绝对安全的代码
type Foo = string | number | boolean;
function controlAlow(foo: Foo): void {
if (typeof foo === "string") {
} else if (typeof foo === "number") {
} else if (typeof foo === "boolean") {
} else {
const check: never = foo;
}
}type 定义的类型可以通过交叉类型(&)来进行复用,而 interface 定义的类型则可以通过继承(extends)来实现复用;type 和 interface 定义的类型也可以相互复用
// 复用type定义的类型
type Point = {
x: number;
y: number;
};
type Coordinate = Point & {
z: number;
};
// 复用interface定义的类型
interface Point {
x: number;
y: number;
}
interface Coordinate extends Point {
z: number;
}
// type复用interface定义的类型
interface Point {
x: number;
y: number;
}
type Coordinate = Point & {
z: number;
};
// interface复用type定义的类型
type Point = {
x: number;
y: number;
};
interface Coordinate extends Point {
z: number;
}例如:有一个已知的类型 Props 需要复用,但是不需要其中的某一个属性 a,这种情况,我们可以使用 Omit 来高效的实现复用
// Omit 忽略某个类型
interface Props {
a: string;
b: number;
c: boolean;
}
type OmitProps = Omit<Props, "a"> & {
e: string;
};
interface OmitProps1 extends Omit<Props, "a"> {
e: string;
}类似的,工具类型 Pick 也可以用于实现此类复用
// Pick 包含某个类型
interface Props {
a: string;
b: number;
c: boolean;
}
interface Props1 extends Pick<Props, "a" | "b"> {
d: string;
}// pick 类型
type Pick<T, U extends keyof T> = {
[P in U]: T[P];
};使用场景
在开发组件库时,我们经常会面临相似功能的组件属性命名不一致的问题,例如,用于表示组件是否展示的属性,可能被命名为 show、visible、isShow 等,这样不仅影响组件库的易用性,也降低了可维护性;
为了解决这一问题,定义一套统一的基础类型至关重要
import { CSSProperties } from "react";
type Size = "small" | "middle" | "large";
type Baseprops<T> = {
/**
* 自定义样式类名
*/
className?: string;
/**
* 自定义样式
*/
style?: CSSProperties;
/**
* children
*/
children?: ReactNode | undefined | any;
onChange: (value: T) => void;
};基于上面的基本类型,定义具体组件的属性变得简单直接
type Type = "primary" | "secondary" | "tertiary" | "warning" | "danger";
interface ButtonProps extends Baseprops<string> {
type: Type;
iconPosition?: "left" | "right";
onClick?: React.MouseEventHandler<HTMLButtonElement>;
loading?: boolean;
}某个变量非常确定类型,通过断言这种方式告诉编译器,这个变量是某个类型;类型断言好比是类型转换
// 尖括号语法
let someValue: any = "hello";
let strLength: number = (<string>someValue).length;
// as语法
let someValue1: any = "hello";
let strLength1: number = (someValue1 as string).length;// 函数类型表达式(Function Type Expressions)
function greeter(fn: (name: string) => void) {
fn("hello");
}
function printToConsole(name: string) {
console.log(name);
}
greeter(printToConsole);
// 函数类型
let IdGenerator: (chars: string, nums: number) => string;
function createUserID(name: string, id: number): string {
return name + id;
}
IdGenerator = createUserID;
// 可选参数及默认参数
function createUserID1(name: string, id: number, age?: number): string {
return name + id;
}
function createUserID2(
name: string = "default",
id: number,
age?: number
): string {
return name + id;
}
// 剩余参数
function myPush(array: number[], ...items: number[]) {
items.forEach((item) => {
array.push(item);
});
}
myPush([1, 2], 1, 2, 3);
// 参数解构
type ABC = {
a: number;
b: string;
c: boolean;
};
function DesParams({ a, b, c }: ABC) {
console.log(a, b, c);
}
function DesParams1({ a, b, c }: { a: number; b: string; c: boolean }) {
console.log(a, b, c);
}
DesParams({ a: 1, b: "2", c: true });
DesParams1({ a: 1, b: "2", c: true });
// 函数重载1: 普通函数
function add(a: number, b: number): number;
function add(a: number, b: string): string;
function add(a: string | number, b: string | number) {
if (typeof a === "string" || typeof b === "string") {
return a.toString() + b.toString();
}
return a + b;
}
add(1, 2);
add(1, "2");
// 函数重载2 对于箭头函数,虽然它们不直接支持函数重载,但我们可以通过定义函数签名的方式来实现类似的效果
type GreetFunction = {
(name: string): string;
(age: number): string;
}
const great: GreetFunction = (value: any): string => {
if (typeof value === 'string') {
return 'hello ' + value;
} else if (typeof value === 'number') {
return `You are ${value} years old.`;
}
return '';
}
// 处理含有不同类型元素的数组
function useMyHook(): [string, number] {
return ["hello", 1];
}
function MyComponent() {
const [text, num] = useMyHook();
console.log(text); // 输出字符串
console.log(number); // 输出数字
return null;
} 属性继承
interface BaseAddressInfo {
name?: string;
street: string;
city: string;
country: string;
postalCode: string;
}
interface ExtendsAddressInfo extends BaseAddressInfo {
unit: string;
}交叉类型
interface ColorFul {
color: string;
}
interface Circle {
radius: number;
color: number; // 交叉之后color类型为never
}
type ColorFulCircle = ColorFul & Circle;像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据
泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
const fn: GenericIdentityFn<string> = (arg: string) => {
return arg;
};泛型类
// tsconfig.json strictPropertyInitialization 配置需要调整
class GenericClass<T> {
zeroValue: T;
add: (a: T, b: T) => T;
}
let myGenericClass = new GenericClass<number>();
myGenericClass.zeroValue = 1;
myGenericClass.add = function (x, y) {
return x + y;
};泛型变量 常见泛型变量代表的意思
泛型工具类型
// typeof
interface Person {
name: string;
age: number;
}
const sem: Person = {
name: "yyy",
age: 30,
};
type Sem = typeof sem; // Person
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray; // (x: number) => number[]
// keyof
type k1 = keyof Person; // "name" | "age";
type k2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type k3 = keyof { [x: string]: Persion }; // "string" | "number"
// in
type Keys = "a" | "b" | "c";
type KeysEn = {
[p in Keys]: string;
}; // { a: string, b: string, c: string }
// infer
// 在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
// 以上代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用
// extends
// 有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束
interface ILengthwise {
length: number;
}
function loggingIdentity<T extends ILengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity({ length: 10, value: 3 });
// Partial
// Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 示例
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
const todo1 = {
title: "organize desk",
description: "clear clutter",
};
const todo2 = updateTodo(todo1, {
description: "throw out trash",
});TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称
keyof 操作符可以用于获取某种类型的所有键,其返回类型是联合类型
// 使用1
interface PersonInfo {
name: string;
age: number;
location: string;
}
type PersonInfoTypes = keyof PersonInfo;
// type PersonInfoTypes = "name" | "age" | "location";
// 使用2
type Point = { x: number; y: number };
type P = keyof Point;
// type P = "x" | "y";
// 使用3
class PersonClass {
name: string = "sImss";
}
let sname: keyof PersonClass;
sname = "name";
// 使用4
type Todo = {
id: number;
text: string;
done: boolean;
};
const todo: Todo = {
id: 1,
text: "Learn TypeScript",
done: false,
};
function props<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const resid = props(todo, "id");
const restext = props(todo, "text");
const resdone = props(todo, "done");// typeof
typeof someValue === "symbol";
const person = {
name: "yyy",
age: 18,
};
type Person = typeof person;
// type Person = {
// name: string;
// age: string;
// }
function identity<Type>(arg: Type): Type {
return arg;
}
type result = typeof identity;
// type result = <Type>(arg: Type) => Type
enum UserResponse {
NO = 0,
YES = 1,
}
type userResult = typeof UserResponse;
const tempa: userResult = {
YES: 1,
NO: 0,
};class Greeter {
// 静态属性
static cname: string = "Greeter";
// 成员属性
greeting: string;
// 构造函数 - 执行初始化操作
constructor(message: string) {
this.greeting = message;
}
// 静态方法
static getClassName() {
return "Class name is Greeter";
}
// 成员方法
greet() {
return "Hello, " + this.greeting;
}
// 通过 getter 和 setter 方法来实现数据的封装和有效性校验,防止出现异常数据
private _name: string;
get fullName(): string {
return this._name;
}
set fullName(newName: string) {
this._name = newName;
}
}
let greeter = new Greeter("world");class CarConstructor {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Car extends CarConstructor {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let car = new Car("BMW");
car.move(10);class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, my name is ${this.#name}!`);
}
}
let semlinker = new Person("Semlinker");
semlinker.#name;function getData(): Promise<{ name: string }[]> {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ name: "Semlinker" }, { name: "Semlinker1" }]);
}, 200);
});
}
type t = Awaited<ReturnType<typeof getData>>;
// Awaited<ReturnType<T>>获取函数的返回类型{
"compilerOptions": {
/* 基本选项 */
"target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
"module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
"lib": [], // 指定要包含在编译中的库文件
"allowJs": true, // 允许编译 javascript 文件
"checkJs": true, // 报告 javascript 文件中的错误
"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
"declaration": true, // 生成相应的 '.d.ts' 文件
"sourceMap": true, // 生成相应的 '.map' 文件
"outFile": "./", // 将输出文件合并为一个文件
"outDir": "./", // 指定输出目录
"rootDir": "./", // 用来控制输出目录结构 --outDir.
"removeComments": true, // 删除编译后的所有的注释
"noEmit": true, // 不生成输出文件
"importHelpers": true, // 从 tslib 导入辅助工具函数
"isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
/* 严格的类型检查选项 */
"strict": true, // 启用所有严格类型检查选项
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
"strictNullChecks": true, // 启用严格的 null 检查
"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
/* 额外的检查 */
"noUnusedLocals": true, // 有未使用的变量时,抛出错误
"noUnusedParameters": true, // 有未使用的参数时,抛出错误
"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
/* 模块解析选项 */
"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
"baseUrl": "./", // 用于解析非相对模块名称的基目录
"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
"typeRoots": [], // 包含类型声明的文件列表
"types": [], // 需要包含的类型声明文件名列表
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
/* Source Map Options */
"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
/* 其他选项 */
"experimentalDecorators": true, // 启用装饰器
"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
}
}