TypeScript 和 VS Code 入门技巧:10 个我希望当初就知道的事情
TypeScript 本应让你的开发工作更轻松,但我仍然清晰地记得我最初接触 TypeScript 时的情景:满嘴脏话,对各种错误感到困惑,并且不明白为什么有人会浪费时间去编写所有类型,以及接口究竟比属性类型好在哪里。我曾经花几个小时编写类型定义,然后抱怨说我的工作效率丝毫没有提高。
我当时的想法可以理解:我完全是个新手(或者用委婉的说法,是个初学者)。新手做什么都难,而且容易让人沮丧。如今,我热爱 TypeScript,而且通常觉得用纯 JavaScript 编写的项目会损害我的心理健康。回想起来,我早期可能很讨厌 TypeScript,因为我必须克服重重障碍才能感受到它的好处。
如果你最近加入了一个 TypeScript 项目,或者正考虑学习 TypeScript 并将其应用到自己的工作中,那么我希望这篇短文能帮助你更轻松地过渡。这些内容或许正是我当初开始学习 TypeScript 时希望自己知道却无人提及的知识。
无需赘言,以下是一些实用且酷炫的技巧,它们能让您在使用 TypeScript 时更加轻松。
1. 用于Ctrl + Space查看组件的所有可用属性,或对象的所有可用键。
别犯傻了。在文件间切换或者谷歌文档是那些闲得没事干的人才会做的事 ;) 直接在代码下方的下拉菜单中查看所有可用变量要方便得多。
如果你使用的是常用的库,十有八九变量名都足够清晰明了,能让你明白它的用途。如果你对自己的代码(或者同事的代码!)中某个变量的功能感到困惑,那么你就能立刻看出哪些变量可能需要重命名,甚至重构。
as2.当你比代码检查工具更了解变量的类型时使用。
这种情况我们都遇到过。你试图传入一个可能是字符串的变量,TypeScript 会警告你它也可能未定义,这显然是不可接受的。但你明明知道这个变量在你使用它的地方永远不会是未定义的,因为你很久以前就处理过这个问题了。而且,你可能也不想写一大堆 if 语句。
最简单的解决方法就是使用as:
interface Props { word?: string }
// let's say that we definitely passed in
// a word to props in another file
const { word } = props;
const logWord = (word: string) => {
console.log(`The word is: ${word}`)
}
// woops, Typescript error! word is required!
logWord(word);
// no error
logWord(word as string)
上面的例子可能会让人觉得使用as`prop` 属性一定是不好的做法。既然以后还需要用到它,为什么还要把它设为可选呢?没错,但这只是因为我暂时想不到更好的例子。实际上,这个问题经常出现,尤其是在使用第三方库的时候。
迟早有一天,你可能会遇到这样的情况:在你的思维工具包中掌握它非常有用as,而且使用它as解决问题的速度比你坚持让你的代码逻辑绝对万无一失并且对 TypeScript 友好要快 5 倍。
这种方法as称为类型断言,也可以通过在变量前用括号注明类型来实现:
// type assertion with 'as'
const wordLength = (word as string).length;
// type assertion with brackets
const wordLength = (<string>word).length
3. 使用 ` Pickand`Omit高效地创建类型化子组件
为每个子组件编写接口就是在重复自己。正如每一本优秀的软件开发书籍都会告诉你的那样,编程的第一条原则就是:不要重复自己!
试试这个:
interface PersonProps {
name: string,
age: number,
imageUrl: string,
location: string,
description: string,
}
const PersonContainer = (props: PersonProps) => {
const { name, age, imageUrl, location, description } = props;
return (
<div>
<img src={imageUrl} />
<h2>{name}, {age}</h2>
<h3>{location}</h3>
<p>{description}</p>
</div>
);
}
/**
* Let's say you only want to display some of the
* person's info in a summary card, which can be
* pressed for details
*/
type CardProps = Pick<PersonProps, 'name' | 'age' | 'imageUrl' >;
const PotentialRomanticInterest = (props: CardProps) => {
const { name, age, imageUrl } = props;
return (
<div>
<img src={imageUrl} />
<h2>{name}, {age}</h2>
</div>
);
}
/**
* What if you've got a lot of props, but you want
* to leave some out?
*
* You can make a new type/interface with all
* the same props as your other interface (here,
* PersonProps), but with some omitted.
*/
type BlindDateProfile = Omit<PersonProps, 'imageUrl'>;
const PotentialRomanticInterest = (props: BlindDateProfile) => {
const { name, age, location, description } = props;
return (
<div>
<img src={'images/incognito-image.png'} />
<h2>{name}, {age}</h2>
<h3>{location}</h3>
<p>{description}</p>
</div>
);
}
还有更多类似的选择器。`is`Pick可能Omit是最简单也最常用的选择器,但还有许多其他有用的选择器,例如 `is` Record、Partial`is` 或 ` Parametersis`。如果您想深入了解,请查看TypeScript 的实用类型文档。
4.Cmd + click关于他们想要探索的事物以及他们的期望。
好吧,这其实算不上什么 TypeScript 技巧。但典型的 TypeScript 开发者可能经常会花大量时间用快捷键点击各种第三方库函数。对于第三方库,我发现与其阅读文档,不如直接点击 TypeScript 定义来弄清楚函数的各个参数用途,这样要容易得多。
Cmd + click应该能让你了解这种类型的全部内容。通常你甚至不需要离开当前打开的标签页,但如果你想的话也可以!只需点击弹出窗口的标题即可浏览完整文件。
不要害怕类型定义文件。它们是为代码检查工具准备的,但如果你愿意关注它们,它们对你也很有帮助。
另一个技巧:假设你正在使用一个第三方库函数,它接收一个复杂的参数对象。通常,你可以直接导入参数对象的类型,将你的变量定义为相同的类型,然后系统会友好地发出警告,直到你填写完所有缺失的属性。(如果参数类型没有导出,你也可以使用内置Parameters类型来获取它们。)
import { doSomething } from 'cool-library';
// These types are often exported, but if not, don't fear! `Parameters<>` can usually help you out
type DoSomethingArgs = Parameters<typeof doSomething> // <-- an array;
type FirstArg = DoSomethingArgs[0] // <-- the first argument;
const input: FirstArg = { complicatedKey: 'complicatedValue' } // <-- now this object will have autocomplete, and red warning squiggles :)
doSomething(input);
如果将此技巧与技巧 1 中的自动完成技巧和技巧 6 中的自动导入技巧结合使用,效果会更好。
5. 如果您有一系列选项,请尝试编写自己的类型,而不是使用通用string类型number。
假设你向函数传递了一些名称:
// okay
interface Props {
name: string
}
这样做可行。但如果您知道您的名称只能是以下三个选项之一,而不能是其他任何选项呢?或许您希望 TypeScript 错误提示在您传入不可接受的名称时发出警告。
// better
type StarterPokemon = 'Bulbasaur' | 'Charmander' | 'Squirtle';
interface Props {
name: StarterPokemon
}
这样,该name属性只能是类型中指定的三个字符串之一StarterPokemon。
编写更严格的自定义类型很有用,因为你之后可能会忘记有哪些选项可用,或者其他开发者接手项目后可能不知道哪些宝可梦可以作为初始宝可梦,从而在某个地方违反规则。拥有严格的选项列表可以防止这种情况发生:
const chooseStarterPokemon = (name: StarterPokemon) => {
setPokemon(name);
}
chooseStarterPokemon('Pikachu'); // error!
// Type '"Pikachu"' is not assignable to type 'StarterPokemon'.
另一个好处是,以后无论在哪里使用该字体,只需将鼠标悬停在上面StarterPokemon即可查看所有选项。您甚至可以添加注释:
type StarterPokemon = 'Bulbasaur' // grass
| 'Charmander' // fire
| 'Squirtle' // water
;
/**
* Make sure to add that finaly semi-colon,
* or else the mouse-over info will stop after
* 'Squirtle' and won't show the final comment
*/
一般来说,你可以尝试先编写好类型,这样之后就可以利用control + space第一条建议中的自动补全功能来完成整个项目。借用爱因斯坦的话来说,尽量让你的类型尽可能严格,但也不要过于严格。
6. 自动导入所有缺失的变量Cmd + .
许多 VS Code 用户都知道,只需输入前几个字符,然后选择 VS Code 下拉菜单中的第一个建议,即可自动导入单个内容。但您也可以导入文件中已声明但缺失的所有变量。
如果您使用的是 TypeScript,只需点击未定义的变量/函数/类型,然后按Cmd + .. 即可。VS Code 会为您提供一些导入选项。如果您有多个未定义的项,它甚至会提示您“添加所有缺失的导入”。
这个技巧对第三方库和你自己的代码都有效,只要所有内容都正确导出并且是用 TypeScript 编写的。而且它几乎总是能完全按照预期工作。
事实上,大多数情况下,我甚至都不会检查导入的库是否正确。而且,你通常很快就能发现 VS Code 经常从错误的地方导入哪些东西。例如,它经常从错误的库导入 Lodash 的 `get` 函数,所以这是我少数需要手动导入的东西之一。
7. 使用运算符以更智能的方式获取可选属性?(“可选链式调用”)
你曾经做过这样的事吗?
interface Item {
prop?: {
subprop?: string;
}
}
const value = item && item.prop && item.prop.subprop || 'default';
看到好几个&&这样的嵌套,代码乍一看很难理解。如果能告诉 TypeScript 只有在 ` itemand` 也prop确实存在的情况下才获取深层嵌套的值,岂不是更好?
原来真的有!试试这个:
const value = item?.prop?.subprop || 'default';
该?运算符是一种巧妙的方法,可以告诉 TypeScript(以及较新版本的 JavaScript)仅在上层键存在时才向下查找。换句话说,仅prop当`is` 已定义且为对象时才查找,并且仅当 ` is` 已定义且为对象时才item查找。subpropprop
这种语法的正式名称是“可选链式调用”。它简单易懂,而且很可能会帮你节省几行代码。
allowJs8. 通过启用以下功能来处理旧的 JS 文件tsconfig.json
这是一个简单快捷的小技巧,但对于初学者来说可能并不明显。
你可以轻松地将 TypeScript 文件和普通的 JavaScript 文件混合使用。你只需要对tsconfig.json文件进行一些简单的修改:
{
"compilerOptions": {
"allowJs": true,
// ... other options
}
}
现在你的 TypeScript 编译器不会再阻碍你的开发了。但是你可能需要any为导入的任何 JS 文件使用大量的类型定义。
9. 自动将类型化对象重构为命名接口
假设你有一个函数,它接收一个 long 类型的对象:
renderFAQItem = (item: { faqQuestion?: string, faqAnswer?: string }) => {
return (
// component
)
}
这个{ faqQuestion?: string, faqAnswer?: string }定义使得这行代码过长。更糟糕的是,它还会降低代码的可维护性。
幸好,VS Code 允许你一键将其转换为命名界面:
如果你知道怎么用,那个灯泡其实非常实用。它还可以将变量重构为常量,将代码行重构为函数,为类生成 get/set 方法等等。VS Code TypeScript 文档中有一整节专门介绍重构。
10. 将 TypeScript 与 JSDoc 结合使用,即可在 VS Code 中创建出色的文档。
JSDoc对于任何 JavaScript 项目来说都是一项非常棒的技能。而且幸运的是,它还可以与类型定义结合使用。
当您使用以两个星号开头的多行注释时,VS Code 会自动识别 JSDoc 注释/** Insert content here */。您还可以添加一些关键字(以 @ 符号标记)以获得更强大的智能提示。JSDoc 注释的妙处在于,与普通//注释不同,您输入的内容会在鼠标悬停时显示在项目的任何位置!
在团队协作中,如果你想让其他开发人员更清楚地了解你的代码应该做什么以及如何使用它,那么这对于你的代码库来说是一个很好的补充。
许多优秀的库,例如Material UI,都会在其 TypeScript 定义中添加 JSDoc 注释,这样您就可以直接在 VSCode 标签页中查看属性的文档,而无需打开任何链接。当然,它们通常也会提供链接,以防您需要了解更多信息 :)
以下是我希望在开始学习 TypeScript 时就知道的主要事项。
请记住,TypeScript 的设计初衷是为了帮助你,而不是为了给你添麻烦。一般来说,我会尽量以方便后续代码自动补全的方式编写 TypeScript 代码。
希望这些建议对你有所帮助。祝你好运,玩得开心!
文章来源:https://dev.to/deckstar/typescript-vs-code-tricks-for-beginners-10-things-i-wish-i-knew-when-starting-out-15la