前言
在学习 typescript
的过程中,我希望将学习的内容记录下来,但是与其将该篇文章作为自己学习的笔记,不如将其写为一种注意问题样子的东西,能让我在后续开发过程中,逐步增加、完善里面的问题,成为一个开发中的小册子。
注意:因为目标是一份类似于注意方面和思考问题的册子,所以本文含有大量的主观臆断,不一定适合所有场景,请酌情参考。
开发注意问题
1. 字符串拼接采用模版字符串
采用模版字符串代替拼接:
let myName: string = 'Tom'
// not good
let sentence: string = "Hello, my name is "+ myName
// this is better
let sentence: string = `Hello, my name is ${myName}`
使用 ES6 中的模板字符串,${expr}
用来在模板字符串中嵌入表达式。
2. 对象尽量不要赋空(惰性初始化)
在真正开发过程中,很少会出现对象赋空的情况,请尽量避免赋空。
如果真的在开始初始化的时候,无法直接给值,但是又一定要初始化的时候,也尽量不要直接赋空,这边建议采用惰性初始化。
let foo = {};
foo.bar = 123; // Error: Property 'bar' does not exist on type '{}'
foo.bas = 'Hello World'; // Error: Property 'bas' does not exist on type '{}'
采用 interface
惰性初始化
interface Foo {
bar: number;
bas: string;
}
let foo = {} as Foo;
foo.bar = 123;
foo.bas = 'Hello World';
注意:采用
typescript
的类型断言机制可以通过编译,但是这样做其实在绕过类型检查机制,我们不建议采用et foo = {} as any; foo.bar = 123; foo.bas = 'Hello World';
3. 少用甚至不用Any
尽量在一开始就判断一个数据到底是那种类型,赋值可以在后面赋值,但是声明尽量在初始化的时候就判定好,而不是采用 any
或者类型推断跳过。
// not good
let something: any;
something = 'seven';
// this is better
let something!: string;
something = 'seven';
注意:如果是类似于
Dart
、Kotlin
这种语言,对这种初始化并不会开始赋值的情况,会有一种lateinit
机制,但是这个TypeScript
暂时没有类似的关键字,类似的就是采用 Strict Class Initialization 方法
4. 少用类型推断(类型推论)
如果一个变量类型不会发生改变,我们尽量初始化就给他定义好属性,这样子后续在赋值类型不对的变量的时候会报错。
let myNumber: string;
myNumber = 'seven';
myNumber = 7; // this is wrong!
如果这个地方采用类型推断,则不会报错,也不会有提醒,这并不是我们想要的。
注意:如果一个值真的可能会有两种类型,这种情况下可以采用联合类型的方式:
let myNumber: string | number;
但是如果不是强制的,这个地方考虑将这个变量拆分为两个变量是更好的选择。
5. 不应该采用接口定义数组
定义一个数组,应当符合直观、简洁的办法:
// 直接指定
let fibonacci: number[] = [1, 1, 2, 3, 5];
// 采用泛型指定
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
这两个办法都是很好的定义数组的办法,简洁直观。
不建议,也不应该采用接口描述:
// 这并不建议
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
注意:并非所有情况下,上面两种都可以,有一种情况下(类数组),采用接口描述数组更加好。严格来说,类数组并非数组类型(例如
arguments
)。
6. 正确的函数声明
我认为函数包含几个重点:输入、输出。
因此,声明正确的函数应当规范好,输入的规范,和输出的规范:
function sum(x: number, y: number): number {
return x + y;
}
如果输出的是一个对象,建议采用 interface
去定义返回的类型。
interface person {
name: string;
age: number;
}
function input(name: string, age: number): person {
return { name, age };
}
7. 慎用类型断言( as )
类型断言有些时候很好用,使用 as any
在大多数情况下能让编译器通过我们的代码,这很便利。
但是需要注意的是,类型断言只能够「欺骗」TypeScript
编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误。因此使用断言前考虑以下几点:
- 该类型后续是否会调用方法,或者深层次的属性引用,如果有,请慎用。
-
as any
很有可能掩盖了真正的错误,所以如果不是非常确定,就不要使用as any
。 - 类型断言并非是类型转换,它并非会真正影响变量的真正类型,如果想要转换,请采用类型转换方法。
- 在该场景下,是否可以采用泛型,如果可以采用泛型,请尽量不要采用断言。
8. 为每个组件Props添加interface
只要是组件,并且符合复用的情况的话,都应该为 props
添加 interface
,简单的可以直接采用如下方式:
export default (props: { title: string; certType: string; certInstitution: string }) => {
//.....
如果遇到复杂情况,或者含有嵌套关系,建议写明 interface
并做好注释标记。
interface wrapperProps {
// 组件名字
title: string,
// 子组件渲染需要的参数
renderData: {
data: Date,
loaction: string,
unit: string,
}
// 请求回调
success?: () => void
}
const WrapperComponet = (props: wrapperProps)=>{
// .......