本文旨在巩固ts语法,详细请看官网:
- ts是js的超集
- ts好处:
- 类型检查
- 提高代码可读性、可维护性
- 代码规范和一致性
- 易于代码重构
- 基础类型:
-
布尔值
const isDone: boolean = false; -
数字
const num: number = 0; -
字符串
const str: string = ""; -
数组
const arr: number[] = [1, 2, 3, 4]; -
元组
const Tuple: [number, string, boolean] = [1, "4", false]; -
枚举
enum Color { Red = "red", Green = "green", Blue = "blue", } const ColorGreen: Color = Color.Green; -
任意值 any 代表任意类型,不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查
-
空值
const fuc = (): void=>{ //void 表示没有返回值 } -
Null、undefined
let u: undefined = undefined; let n: null = null; -
never 表示的是那些永不存在的值的类型
-
- 类型断言
let X = 'hello' as string或者let X = <string>'hello'有的时候系统识别不出来赋值,需要手动断言,给属性赋予类型 - 接口
// 接口
interface Person {
readonly name: string;
readonly children?: Person;
//给后代也赋予Person接口,但是children属性,
//可以不存在,所以加个`?`表示不一定需要这个属性
//readonly 表示不可修改
}
let LiMing: Person = {
name: "LiMing",
children: { name: "xiaohong" },
};
//继承 extends
interface student extends Person {
score: number;
grade: number;
}
const xiaoDong: student = {
name: "xiaoDong",
score: 100,
grade: 2,
};
- 类
类继承接口写法例子
interface Person {
readonly name: string;
readonly children?: Person;
}
interface student extends Person {
score: number;
grade: number;
//注意可选属性的写法
sayHello?(): string;
}
class Student implements student {
name: string;
score: number;
grade: number;
constructor(name: string) {
this.name = name;
this.score = 100;
this.grade = 3;
}
sayHello(): string {
return `Hello, I'm ${this.name}`;
}
}
const wang = new Student("wang");
接口继承类写法例子
// 接口继承类
class Animal {
year: number;
constructor(year: number) {
this.year = year;
}
}
interface dog extends Animal {
settings: {
[key: string]: string | number;
};
applySettings?(): void;
sayHello(): void;
}
let Dog: dog = {
year: 12,
settings: {
color: "red",
size: 10,
},
sayHello() {
console.log("nihao2");
},
};
- 函数
//...剩余变量
function sum(firNum: number, ...numbers: number[]): number {
return firNum + numbers.reduce((total, num) => total + num, 0);
}
let res = sum(1, 2, 3, 4);
const add2 = (x: number, y: number): number => {
return x + y;
};
// 方法的返回类型不写的话,ts编译器会自动识别
- 泛型
泛型函数
function f<T>(name: T): T {
console.log((name as string).length);
return name;
}
let name = f<string>("LUO");
// console.log(name);
泛型接口
interface Stu<T> {
name: T;
school: T;
}
let XX: Stu<string> = { name: "XX", school: "实验小学" };
console.log(XX);
泛型类
class GenericNumber<T> {
zeroValue: T | undefined;
add: ((x: T, y: T) => T) | undefined;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
console.log(myGenericNumber.add(1, 5));
- 类型推论:TypeScript里,在有些没有明确指出类型的地方,类型推论会帮助提供类型
- 高级类型
交叉类型
interface A {
str: string;
}
interface B {
str: undefined | string;
}
let name: A & B = { str: "hahah" };
console.log(name);
联合类型+映射类型
interface A {
str: string;
}
interface B {
str: undefined ;
}
let name: A & B = { str: "hahah" };
console.log(name);
条件类型+映射类型
type StringProperties<T> = {
[K in keyof T & string as T[K] extends string ? K : never]: T[K];
};
interface MyObject {
name: string;
age: number;
address: string;
}
type StringPropsObject = StringProperties<MyObject>;
// type StringPropsObject = {
// name: string;
// address: string;
// };
K in keyof T & string可以看作K in (keyof T & string),keyof操作符用于获取类型T的所有键(属性名)的联合类型,&符号表示交叉类型,将keyof T和string进行交叉操作,获取T里面带有string类型的属性,然后通过k in 遍历as T[K] extends string ? K : never如果T里面的属性有string类型则断言K in keyof T & string划分出来的带有string类型的的属性为string类型- 然后通过映射,将T[K]的类型赋予前面的属性
索引类型
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
interface Car {
brand: string;
model: string;
year: number;
}
const myCar: Car = { brand: "Toyota", model: "Corolla", year: 2020 };
const brand = getProperty(myCar, "brand");
// brand的类型是string
-
在这个
getProperty函数中,<T,K>创建T、K 的类型,K extends keyof T约束了参数key必须是对象obj(类型为T)的有效属性名(keyof T获取了所有有效属性名的联合类型),T[K]确保了函数返回值的类型是对象中对应属性的值类型。这样就避免了因为访问不存在的属性或者返回类型错误而导致的问题 -
命名空间
namespace MyNamespace {
export const myVariable = 1;
export function myFunction() {
console.log("This is a function in MyNamespace.");
}
export class MyClass {
constructor() {
console.log("This is a class in MyNamespace.");
}
}
}
命名空间(Namespace)主要用于组织代码,避免名称冲突。它是一种将相关的代码(如变量、函数、类、接口等)组合在一起的方式,并且可以在命名空间内部定义的名称不会与命名空间外部的同名名称产生冲突。
- Decorators
function classDecorator(constructor: Function) {
console.log("Class decorator is called.");
}
@classDecorator
class MyClass {
constructor() {
console.log("MyClass constructor is called.");
}
}
let classDecorator2 = new MyClass();
装饰器是在特定的时机自动触发的。对于类装饰器,当定义一个类并应用了类装饰器时就会触发,它会接收类的构造函数作为参数进行操作
装饰器的使用,需要在tsconfig.json中配置 "experimentalDecorators": true,
- 混入
- 在面向对象编程中,mixin(混入)是一种代码复用的方式。它允许一个类通过组合(而不是继承)的方式从其他类中获取功能。Mixin 提供了一种灵活的方式来将多个独立的功能模块组合到一个类中,避免了传统多继承可能带来的复杂性和冲突。
// mixin
function LoggingMixin<TBase extends new (...args: any[]) => {}>(Base: TBase) {
return class LoggingClass extends Base {
log(message: string) {
console.log(`Base log`, message);
}
};
}
class User {
constructor(public name: string) {}
}
// 使用mixin为User类添加功能
const LoggedUser = LoggingMixin(User);
const user = new LoggedUser("Alice");
user.log("User logged in");
LoggingMixin是一个函数,它接受一个类(Base)作为参数,并返回一个新的类。这个新的类继承自传入的Base类,并且添加了一个log方法用于记录日志。- 首先定义了
User类,它有一个name属性 - 然后通过
LoggingMixin函数将User类作为参数传入,得到一个新的LoggedUser类, - 这个新类既拥有
User类原来的属性和方法(这里是name属性),又拥有mixin添加的log方法。最后创建了user实例并调用了log方法来记录日志。