清洁代码的 6 个高级 TypeScript 技巧

本文详细介绍了TypeScript的高级特性,包括映射类型、条件类型用于创建新类型,装饰器用于添加元数据和扩展行为,命名空间组织代码,混合实现类的复用,以及类型保护和实用程序类型确保类型安全。这些特性提高了代码的灵活性、可维护性和安全性。
摘要由CSDN通过智能技术生成

1 — 高级类型
TypeScript 中的高级类型(例如映射类型和条件类型)允许您基于现有类型创建新类型。这些类型可以帮助您以强大的方式转换和操作类型,从而提高代码的灵活性和可维护性。

映射类型
映射类型迭代现有类型的属性并应用转换来创建新类型。一个常见的用例是创建一个类型的只读版本。

type Readonly = {
readonly [P in keyof T]: T[P];
};

接口 点{
x :数字;
y:数字;
}

type ReadonlyPoint = Readonly ; _
在此示例中,我们定义了一个名为 的映射类型Readonly,它将一个类型T作为泛型参数并将其所有属性设置为只读。ReadonlyPoint然后我们基于接口创建一个类型Point,其中所有属性都是只读的。

条件类型
条件类型允许您根据条件创建新类型。语法类似于三元运算符,使用extends关键字作为类型约束。

类型 NonNullable = T extends null | 未定义?从不:T;
在此示例中,我们定义了一个名为 的条件类型NonNullable,它接受一个类型T并检查它是否扩展nullor undefined。如果是,则结果类型为never,否则为原始类型T。

让我们扩展高级类型示例以包含更多可用性和输出。

接口 点{
x :数字;
y:数字;
}

type ReadonlyPoint = Readonly ; _

const regularPoint : Point = {
x : 5 ,
y : 10
};

const readonlyPoint : ReadonlyPoint = {
x : 20 ,
y : 30
};

常规点。x = 15 ;// 这是因为 ‘x’ 在 ‘Point’ 界面
控制台中是可变的。日志(常规点);// 输出:{ x: 15, y: 10 }

// readonlyPoint.x = 25; // 错误:无法分配给 ‘x’,因为它是只读属性
console。日志(只读点);// 输出:{ x: 20, y: 30 }

function movePoint ( p: Point, dx: number , dy: number ): Point {
return { x : p. x + dx, y : p。y + dy };
}

常量movedRegularPoint = movePoint(regularPoint, 3 , 4 );
控制台。日志(movedRegularPoint);// 输出:{ x: 18, y: 14 }

// const movedReadonlyPoint = movePoint(readonlyPoint, 3, 4); // 错误:“ReadonlyPoint”类型的参数不可分配给“Point”类型的参数
在此示例中,我们演示了映射类型的用法Readonly以及它如何强制执行不变性。我们创建一个可变Point对象和一个只读ReadonlyPoint对象。我们展示了尝试修改只读属性会导致编译时错误。我们还说明了只读类型不能用于预期可变类型的地方,以防止在我们的代码中产生意外的副作用。

2 — 装饰器
TypeScript 中的装饰器是一项强大的功能,允许您添加元数据、修改或扩展类、方法、属性和参数的行为。它们是高阶函数,可用于观察、修改或替换类定义、方法定义、访问器定义、属性定义或参数定义。

类装饰器
类装饰器应用于类的构造函数,可用于修改或扩展类定义。

函数 LogClass(目标:函数){
控制台。日志(定义了类${target.name} 。` );
}

@LogClass
类 MyClass {
构造函数( ) {}
}
在此示例中,我们定义了一个名为 的类装饰器LogClass,它在定义时记录装饰类的名称。MyClass然后我们使用语法将装饰器应用于类@。

方法装饰器
方法装饰器应用于类的方法,可用于修改或扩展方法定义。

函数 LogMethod(目标:任意,键:字符串,描述符:PropertyDescriptor){
控制台。日志(方法${key}被调用。);
}

类 MyClass {
@LogMethod
myMethod ( ) {
控制台。日志(“在我的方法里面。”);
}
}

const实例 = new MyClass ();
实例。我的方法();
在此示例中,我们定义了一个名为 的方法装饰器LogMethod,它会在调用时记录装饰方法的名称。然后我们使用语法将装饰器应用于类myMethod的方法。MyClass@

财产装饰者
属性装饰器应用于类的属性,可用于修改或扩展属性定义。

function DefaultValue ( value: any ) {
return ( target: any , key: string ) => {
target[key] = value;
};
}

class MyClass {
@DefaultValue ( 42 )
myProperty : number ;
}

const instance = new MyClass ();
控制台。日志(实例。myProperty);// 输出:42
在这个例子中,我们定义了一个名为 的属性装饰器DefaultValue,它为被装饰的属性设置了一个默认值。然后我们使用语法将装饰器应用于类myProperty的属性。MyClass@

参数装饰器
参数装饰器应用于方法或构造函数的参数,可用于修改或扩展参数定义。

function LogParameter ( target: any , key: string , parameterIndex: number ) {
控制台。日志(调用了方法 k e y 的索引 {key}的索引 key的索引{parameterIndex}处的参数。`);
}

类 MyClass {
myMethod(@LogParameter值:数字){
控制台。日志(在 myMethod 中,值为${value}。);
}
}

常量实例 =新 我的类();
实例。我的方法(5);
在这个例子中,我们定义了一个名为 的参数装饰器LogParameter,它在调用方法时记录装饰参数的索引和名称。然后我们使用语法将装饰器应用于类方法value的参数。myMethodMyClass@

3 — 命名空间
TypeScript 中的命名空间是一种组织和分组相关代码的方式。它们帮助您避免命名冲突,并通过封装属于一起的代码来促进模块化。命名空间可以包含类、接口、函数、变量和其他命名空间。

定义命名空间
要定义命名空间,请使用namespace关键字后跟命名空间名称。然后,您可以在花括号内添加任何相关代码。

namespace MyNamespace {
export class MyClass {
constructor ( public value: number ) {}

displayValue ( ) { 
  console . log ( `值为: ${ this .value} ` ); 
} } 

}
在这个例子中,我们定义了一个名称空间 calledMyNamespace并MyClass在其中添加了一个类。请注意,我们使用export关键字使类可以在命名空间之外访问。

使用命名空间
要使用命名空间中的代码,您可以使用完全限定名称或使用命名空间导入来导入代码。

// 使用完全限定名称
const instance1 = new MyNamespace . 我的班级(5);
实例 1。显示值();// 输出:值为:5

// 使用命名空间 import
import MyClass = MyNamespace . 我的班级;

const instance2 = new MyClass ( 10 );
实例2。显示值();// 输出:值为:10
在此示例中,我们演示了两种使用命名空间MyClass中的类的方法MyNamespace。首先,我们使用完全限定名称MyNamespace.MyClass。其次,我们使用命名空间导入语句导入MyClass类并使用更短的名称。

嵌套命名空间
可以嵌套命名空间以创建层次结构并进一步组织您的代码。

namespace OuterNamespace {
export namespace InnerNamespace {
export class MyClass {
constructor ( public value: number ) {}

  displayValue ( ) { 
    console . log ( `值为: ${ this .value} ` ); 
  } 
} 

}
}

// 使用完全限定名称
const instance = new OuterNamespace . 内部命名空间。我的班级(15 );
实例。显示值();// 输出:值为:15
在这个例子中,我们定义了一个InnerNamespace在OuterNamespace. 然后,我们在嵌套命名空间中定义一个类MyClass,并将其与完全限定名称一起使用OuterNamespace.InnerNamespace.MyClass。

4 — 混合
TypeScript 中的混合是一种从多个较小的部分组成类的方法,称为混合类。它们允许您在不同类之间重用和共享行为,促进模块化和代码可重用性。

定义混合
要定义混合类,请创建一个类,该类使用构造函数签名扩展泛型类型参数。这允许 mixin 类与其他类组合。

class TimestampMixin < TBase extends new (… args : any []) => any >( Base : TBase ) {
constructor ( …args: any [] ) {
super (…args);
}

getTimestamp ( ) {
返回 新 日期();
}
}
在此示例中,我们定义了一个名为 mixin 的类,它TimestampMixin添加了一个getTimestamp返回当前日期和时间的方法。mixin 类TBase使用构造函数签名扩展泛型类型参数,以允许它与其他类组合。

使用混合
要使用 mixin 类,定义一个基类并使用关键字将 mixin 类应用于它extends。

类 MyBaseClass {
构造函数(公共值:数字){}

displayValue(){
控制台。log ( 值为: ${ this .value} );
}
}

class MyMixedClass extends TimestampMixin ( MyBaseClass ) {
constructor ( value: number ) {
super (value);
}
}
MyBaseClass在这个例子中,我们定义了一个用方法调用的基类displayValue。然后我们创建一个名为的新类MyMixedClass,它扩展基类并将TimestampMixin混合类应用于它。

让我们演示 mixin 类在实践中是如何工作的。

const instance = new MyMixedClass ( 42 );
实例。显示值();// 输出:值为:42
const timestamp = instance. 获取时间戳();
控制台。log ( 时间戳是:${timestamp} ); // 输出:时间戳为:[当前日期和时间]
在此示例中,我们创建了该类的一个实例MyMixedClass,其中包括displayValue来自 的方法MyBaseClass和getTimestamp来自TimestampMixinmixin 类的方法。然后我们调用这两种方法并显示它们的输出。

5 — 类型保护
TypeScript 中的类型保护是一种缩小特定代码块中变量或参数类型的方法。它们允许您区分不同的类型和访问特定于这些类型的属性或方法,从而促进类型安全并减少运行时错误的可能性。

定义类型保护
要定义类型保护,请创建一个接受变量或参数并返回类型谓词的函数。类型谓词是一个布尔表达式,用于缩小函数范围内参数的类型。

function isString ( value: any ): value is string {
return typeof value === “string” ;
}
在此示例中,我们定义了一个名为 type guard 的函数isString,用于检查给定值是否为 type string。该函数返回一个类型 predicate value is string,它缩小了value函数范围内参数的类型。

使用类型保护
要使用类型保护,只需在条件语句中调用类型保护函数,例如语句if或switch语句。

function processValue ( value: string | number ) {
if ( isString (value)) {
console . log ( 字符串的长度为:${value.length} );
}其他{
控制台。log ( 数字的平方是:${value * value} );
}
}
processValue在这个例子中,我们定义了一个名为 type 的函数string | number。我们使用isString类型保护函数来检查值是否为字符串。如果是,我们访问length特定于该string类型的属性。否则,我们假设该值为 anumber并计算其平方。

让我们演示类型保护在实践中是如何工作的。

过程值(“你好”); // 输出:字符串的长度为:5
processValue ( 42 ); // 输出:数字的平方是:1764
在此示例中,我们processValue使用字符串和数字调用该函数。类型保护功能isString确保为每种类型执行适当的代码块,使我们能够访问特定于类型的属性和方法而不会出现任何类型错误。

6 — 实用程序类型
TypeScript 中的实用类型提供了一种将现有类型转换为新类型的便捷方式。它们允许您创建更复杂和灵活的类型,而无需从头开始定义它们,从而提高代码的可重用性和类型安全性。

使用实用程序类型
要使用实用程序类型,请使用尖括号语法将实用程序类型应用于现有类型。TypeScript 提供了多种内置实用程序类型,例如Partial、Readonly、Pick和Omit。

接口 人{
名称:字符串;
年龄:人数;
电子邮件:字符串;
}

类型 PartialPerson = Partial ; _
类型 ReadonlyPerson = Readonly < Person >;
输入 NameAndAge = Pick < Person , “name” | “年龄” >;
输入 WithoutEmail =省略< Person , “email”>;
在此示例中,我们定义了一个调用的接口,该接口Person具有三个属性:name、age和email。然后我们使用各种内置的实用程序类型来创建基于Person接口的新类型。

让我们演示实用程序类型在实践中是如何工作的。

部分的:

const partialPerson : PartialPerson = {
name : “John Doe” ,
};
在这个例子中,我们创建了一个partialPerson类型的对象PartialPerson。实用类型Partial使接口的所有属性Person都是可选的,允许我们创建一个只有一个name属性的部分人。

只读:

const readonlyPerson : ReadonlyPerson = {
name : “Jane Doe” ,
age : 30 ,
email : “jane@example.com” ,
};

// readonlyPerson.age = 31; // 错误:无法分配给 ‘age’ 因为它是只读属性
在这个例子中,我们创建了一个readonlyPerson类型的对象ReadonlyPerson。utilityReadonly类型让Person接口的所有属性都变成只读的,防止我们修改age属性。

挑选:

const nameAndAge : NameAndAge = {
name : “John Smith” ,
age : 25 ,
};

// 名字和年龄.email; // 错误:属性 ‘email’ 在类型 ‘Pick<Person, “name” 上不存在 | “年龄”>’
在这个例子中,我们创建了一个nameAndAge类型的对象NameAndAge。实用程序类型Pick创建一个新类型,该类型仅具有接口的指定属性Person,在本例中name为 和age。

忽略:

const withoutEmail : WithoutEmail = {
name : “Jane Smith” ,
age : 28 ,
};

// withoutEmail.email; // 错误:‘Omit<Person, “email”>’ 类型不存在属性 ‘email’
在这个例子中,我们创建了一个withoutEmail类型的对象WithoutEmail。实用程序Omit类型通过从接口中删除指定的属性来创建新类型Person,在本例中为email.

总之,本文探讨了各种高级 TypeScript 主题,例如命名空间、高级类型、装饰器、混合、类型保护和实用程序类型。通过理解和利用这些特性,您可以创建更加模块化、可重用和可维护的代码,这些代码遵循最佳实践并减少运行时错误的可能性。

通过利用这些高级 TypeScript 功能,您可以编写更清晰、更有条理且可维护的代码,从而充分利用 TypeScript 强大的类型系统和语言功能。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q shen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值