? 这是第 84 篇不掺水的原创,想要了解更多,请戳上方蓝色字体:政采云前端团队 关注我们吧~
本文首发于政采云前端团队博客:编写高质量可维护的代码:Awesome TypeScript https://www.zoo.team/article/awesome-typescript
高质量可维护的代码应具备可读性高、结构清晰、低耦合、易扩展等特点。而原生的 JavaScript 由于其弱类型和没有模块化的缺点,不利于大型应用的开发和维护,因此,TypeScript 也就应运而生。
TypeScript 是 JavaScript 的一个超集,它的设计初衷并不是为了替代 JavaScript,而是基于 JavaScript 做了一系列的增强,包括增加了静态类型、接口、类、泛型、方法重载等等。所以,只要你有一定的 JavaScript 功底,那么 TypeScript 上手就非常简单。并且,你可以在 TypeScript 中愉快的使用 JavaScript 语法。
接下去,本文将给大家分享下,TypeScript 的重要特性以及在实际场景中的使用技巧,帮助大家更高效的编写高质量可维护的代码。
JavaScript
TypeScript
经过上述对比,可以看到 TypeScript 的出现很好的弥补了 JavaScript 的部分设计缺陷,给我们带来了很大的便利,也提高了代码的健壮性和扩展性。
// 包括 数字枚举、字符串枚举、异构枚举(数字和字符串的混合)。
// 数字枚举在不设置默认值的情况下,默认第一个值为0,其他依次自增长
enum STATUS {
PENDING,
PROCESS,
COMPLETED,
}
let status: STATUS = STATUS.PENDING; // 0
let tupleType: [string, boolean];
tupleType = ["momo", true];
:
冒号后面注明变量的类型即可。const str: string = 'abc';
interface Animal {
name: string;
getName(): string;
}
class Monkey implements Padder {
constructor(private name: string) {
getName() {
return 'Monkey: ' + name;
}
}
}
class Person {
// 静态属性
static name: string = "momo";
// 成员属性
gender: string;
// 构造函数
constructor(str: string) {
this.gender = str;
}
// 静态方法
static getName() {
return this.name;
}
// 成员方法
getGender() {
return 'Gender: ' + this.gender;
}
}
let person = new Person("female");
class Person {
private _name: string;
get name(): string {
return this._name;
}
set name(newName: string) {
this._name = newName;
}
}
let person = new Person('momo');
console.log(person.name); // momo
person.name = 'new_momo';
console.log(person.name); // new_momo
class Animal {
name: string;
constructor(nameStr=:string) {
this.name = nameStr;
}
move(distanceInMeters: number = 0) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name);
}
move(distanceInMeters = 5) {
super.move(distanceInMeters);
}
}
let snake = new Snake('snake');
snake.move(); // 输出:'snake moved 5m'
#
字符开头。私有字段不能在包含的类之外访问,甚至不能被检测到。class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}
greet() {
console.log(`Hello, ${this.#name}!`);
}
}
let person = new Person('momo');
person.#name; // 访问会报错
interface identityFn<T> {
(arg: T): T;
}
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
&
运算符定义。如下示例中,将 Person 类型和 Company 类型合并后,生成了新的类型 Staff,该类型同时具备这两种类型的所有成员。interface Person {
name: string;
gender: string;
}
interface Company {
companyName: string;
}
type Staff = Person & Company;
const staff: Staff = {
name: 'momo',
gender: 'female',
companyName: 'ZCY'
};
|
运算符定义。如下示例中,函数的入参为 String 或 Number 类型即可。function fn(param: string | number): void {
console.log("This is the union type");
}
类型保护就是在我们已经识别到当前数据是某种数据类型的情况下,安全的调用这个数据类型对应的属性和方法。常用的类型保护包括 in
类型保护、typeof
类型保护、instanceof
类型保护和 自定义
类型保护。具体见以下示例:
in
类型保护 interface Person {
name: string;
gender: string;
}
interface Employee {
name: string;
company: string;
}
type UnknownStaff = Person | Employee;
function getInfo(staff: UnknownStaff) {
if ("gender" in staff) {
console.log("Person info");
}
if ("company" in staff) {
console.log("Employee info");
}
}
typeof
类型保护 function processData(param: string | number): unknown {
if (typeof param === 'string') {
return param.toUpperCase()
}
return param;
}
instanceof
类型保护:和 typeof
类型用法相似,它主要是用来判断是否是一个类的对象或者继承对象的。 function processData(param: Date | RegExp): unknown {
if (param instanceof Date) {
return param.getTime();
}
return param;
}
自定义
类型保护:通过类型谓词 parameterName is Type
来实现自定义类型保护。如下示例,实现了接口的请求参数的类型保护。 interface ReqParams {
url: string;
onSuccess?: () => void;
onError?: () => void;
}
// 检测 request 对象包含参数符合要求的情况下,才返回 url
function validReqParams(request: unknown): request is ReqParams {
return request && request.url
}
?.
if(result && result.data && result.data.list) // JS
if(result?.data?.list) // TS
??
let temp = (val !== null && val !== void 0 ? val : '1'); // JS
let temp = val ?? '1'; // TS
function fn(value:boolean){
switch(value){
case true:
console.log('true');
break;
case false:
console.log('false');
break;
default:
console.log('dead code');
}
}
// 推荐写法
function getLocalStorage<T>(key: string): T | null {
const str = window.localStorage.getItem(key);
return str ? JSON.parse(str) : null;
}
const data = getLocalStorage<DataType>("USER_KEY");
function create<T>(c: { new(): T }): T {
return new c();
}
class Test {
constructor() {
}
}
create(Test);
function fn(arr:readonly number[] ){
let sum=0, num = 0;
while((num = arr.pop()) !== undefined){
sum += num;
}
return sum;
}
// 使用 const enum 维护常量
const enum PROJ_STATUS {
PENDING = 'PENDING',
PROCESS = 'PROCESS',
COMPLETED = 'COMPLETED'
}
function handleProject (status: PROJ_STATUS): void {
}
handleProject(PROJ_STATUS.COMPLETED)
{
"compilerOptions": {
/* 严格的类型检查选项 */
"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 语句贯穿)
}
}
TypeScript Extension Pack,它集合了我们日常常用的 TypeScript 相关插件:
Ctrl+Opt+o
,Win/Linux 上快捷键 Ctrl+Alt+o
。Ctrl+Opt+V
,Win/Linux 上快捷键 Ctrl+Alt+V
。如果你觉得这篇内容对你挺有启发,我想邀请你帮我两件小事
1.点个「在看」,让更多人也能看到这篇内容(点了「在看」,bug -1 ?)
2.关注公众号「政采云前端团队」,持续为你推送精选好文