打破 10 个 Typescript 编程坏习惯
【CSDN 编者按】好习惯受益终生,坏习惯尽快改掉!
不使用严格模式
例子:不使用严格模式的 tsconfig.json
{"compilerOptions": {"target": "ES2015","module": "commonjs"}}
应该怎样写?启用严格模式
{"compilerOptions": {"target": "ES2015","module": "commonjs","strict": true}}
为什么有些人会那么写?在现有代码库中引入更严格的规则需要很大的时间成本。
为什么不应该这么做?更严格的规则将使得未来修改代码时更加容易,因此可以减少我们修复代码所花费的时间。
使用 || 定义默认值
例子:通过 || 填充可选值
function createBlogPost (text: string, author: string, date?: Date) {return {text: text,author: author,date: date || new Date()}}
应该怎样写?推荐使用 ?? 运算符,或者定义参数级 fallback。
function createBlogPost (text: string, author: string, date: Date = new Date())return {text: text,author: author,date: date}}
为什么有些人会那么写?
?? 操作符在去年才被引入,所以引入之前很多人会使用 || 来设置默认值。当在长函数中间使用值时,可能很难将它们设置为参数默认值。
为什么不应该这么做?
使用 any 作为类型
例子:当你不确定数据结构时,使用了 any 当做数据类型
async function loadProducts(): Promise<Product[]> {const response = await fetch('https://api.mysite.com/products')const products: any = await response.json()return products}
应该怎么写?每当你有想输入 any 的欲望时,都你应该使用unknown` 代替。
async function loadProducts(): Promise<Product[]> {const response = await fetch('https://api.mysite.com/products')const products: unknown = await response.json()return products as Product[]}
为什么有些人会那么写?any 写起来很方便,因为它基本上禁用所有类型检查。通常,甚至在正式输入中也会使用any (例如,上面示例中的 response.json() 被 TypeScript 团队定义为 Promise<any>)。
为什么不应该那么写?这样写基本上禁用所有类型检查。任何 any 类型的类型检查都将被跳过。这样,当类型结构的推断与运行期不一致时,代码错误才会保留出来,很容易引发一些不易发现的 bug。
as SomeType 类型断言
例子:有力地告诉编译器不能推断的类型。
async function loadProducts(): Promise<Product[]> {const response = await fetch('https://api.mysite.com/products')const products: unknown = await response.json()return products as Product[]}
应该怎么写?
function isArrayOfProducts (obj: unknown): obj is Product[] {return Array.isArray(obj) && obj.every(isProduct)}function isProduct (obj: unknown): obj is Product {return obj != null&& typeof (obj as Product).id === 'string'}async function loadProducts(): Promise<Product[]> {const response = await fetch('https://api.mysite.com/products')const products: unknown = await response.json()if (!isArrayOfProducts(products)) {throw new TypeError('Received malformed products API response')}return products}
为什么有些人会那么写?
为什么不应该那么写?
在测试中使用 as any
例子:在编写测试时创建不完整的替代代码。
interface User {id: stringfirstName: stringlastName: stringemail: string}test('createEmailText returns text that greats the user by first name', () => {const user: User = {firstName: 'John'} as anyexpect(createEmailText(user)).toContain(user.firstName)}
应该怎么写?如果你需要为测试而 mock 数据,那就将 mock 逻辑和 mock 的对象放在一起,并使其可重用。
interface User {id: stringfirstName: stringlastName: stringemail: string}class MockUser implements User {id = 'id'firstName = 'John'lastName = 'Doe'email = '[email protected]'}test('createEmailText returns text that greats the user by first name', () => {const user = new MockUser()expect(createEmailText(user)).toContain(user.firstName)}
为什么有些人会那么写?
为什么不应该那么写?
可选属性
例子:将有时存在有时不存在的属性标记为可选:
interface Product {id: stringtype: 'digital' | 'physical'weightInKg?: numbersizeInMb?: number}
应该怎么写?显式地进行属性组合
interface Product {id: stringtype: 'digital' | 'physical'}interface DigitalProduct extends Product {type: 'digital'sizeInMb: number}interface PhysicalProduct extends Product {type: 'physical'weightInKg: number}
为什么有些人会那么写?
为什么不应该那么写?
只用一个字母描述泛型
function head<T> (arr: T[]): T | undefined {return arr[0]}
应该怎么写?给出完整的描述性类型名称
function head<Element> (arr: Element[]): Element | undefined {return arr[0]}
为什么有些人会那么写?
为什么不应该那么写?
非布尔类型用作布尔检查
例子:通过将值直接传递给 if 语句来检查是否定义了值。
function createNewMessagesResponse (countOfNewMessages?: number) {if (countOfNewMessages) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
应该怎么写?显式地检查我们关心的条件。
function createNewMessagesResponse (countOfNewMessages?: number) {if (countOfNewMessages !== undefined) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
为什么有些人会那么写?
为什么不应该那么写?
!! 操作符
例子:将非布尔值转换为布尔值。
function createNewMessagesResponse (countOfNewMessages?: number) {if (!!countOfNewMessages) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
应该怎么写?显式地检查我们关心的条件。
function createNewMessagesResponse (countOfNewMessages?: number) {if (countOfNewMessages !== undefined) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
为什么有些人会那么写?
为什么不应该那么写?
!=null
例子:!=null 可以同时检查 null 和 undefined
function createNewMessagesResponse (countOfNewMessages?: number) {if (countOfNewMessages != null) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
应该怎么写?显式地检查我们关心的条件。
function createNewMessagesResponse (countOfNewMessages?: number) {if (countOfNewMessages !== undefined) {return `You have ${countOfNewMessages} new messages`}return 'Error: Could not retrieve number of new messages'}
