

爆肝一周,写给初级前端快速转TypeScript指南
source link: https://segmentfault.com/a/1190000040582994
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、为什么要用TypeScript
TypeScript
可以让我们开发中避免一些类型
或者一些不是我们预期希望的代码结果
错误。xxx is not defined 我们都知道JavaScript
错误是在运行中才抛出的,但是TypeScript
错误直接是在编辑器里告知我们的,这极大的提升了开发效率,也不用花大量的时间去写单测,同时也避免了大量的时间排查Bug
。
二、TypeScript优缺点
- 一般我们在前后端联调时,都要去看接口文档上的字段类型,而
TypeScript
会自动帮我们识别当前的类型。节省了我们去看文档
或者network
时间。这叫做类型推导(待会我们会讲到) - 友好地在编辑器里提示错误,避免代码在运行时类型隐式转换踩坑。
- 有一定的学习成本,
TypeScript
中有几种类型概念,interface接口
、class类
、enum枚举
、generics泛型
等这些需要我们花时间学习。 - 可能和一些插件库结合的不是很完美
三、TypeScript运行流程及JavaScript代码运行流程
1. JavaScript运行流程如下,依赖NodeJs环境和浏览器环境
- 将
JavaScript
代码转换为JavaScript-AST
- 将
AST
代码转换为字节码 - 运算时计算字节码
2. TypeScript运行流程,以下操作均为TSC操作,三步执行完继续同上操作,让浏览器解析
- 将
TypeScript
代码编译为TypeScript-AST
- 检查
AST
代码上类型检查 - 类型检查后,编译为
JavaScript
代码 JavaScript
代码转换为JavaScript-AST
- 将
AST
代码转换为字节码 - 运算时计算字节码
四、TypeScript和JavaScript区别
只有搞懂了二者的区别,我们才可以更好的理解TypeScript
JavaScript
JavaScript
动态绑定类型,只有运行程序才能知道类型,在程序运行之前JavaScript
对类型一无所知
TypeScript
TypeScript
是在程序运行前(也就是编译时)就会知道当前是什么类型。当然如果该变量没有定义类型,那么TypeScript
会自动类型推导出来。
JavaScript
比如在JavaScript
中1 + true
这样一个代码片段,JavaScript
存在隐式转换,这时true
会变成number
类型number(true)
和1相加。
TypeScript
在TypeScript
中,1+true
这样的代码会在TypeScript
中报错,提示number
类型不能和boolean
类型进行运算。
何时检查类型
JavaScript
在JavaScript
中只有在程序运行时才能检查类型。类型也会存在隐式转换,很坑。
TypeScript
在TypeScript
中,在编译时就会检查类型,如果和预期的类型不符合直接会在编辑器里报错、爆红
何时报告错误
JavaScript
在JavaScript
只有在程序执行时才能抛出异常,JavaScript
存在隐式转换,等我们程序执行时才能真正的知道代码类型是否是预期的类型,代码是不是有效。
TypeScript
在TypeScript中
,当你在编辑器写代码时,如有错误则会直接抛出异常,极大得提高了效率,也是方便。
五、TypeScript总共围绕两种模式展开
显式注解类型
let name: string = "前端娱乐圈"; let age: number = 38; let hobby: string[] = ["write code", "玩游戏"]
显式注解类型就是,声明变量时定义上类型(官方话语就是声明时带上注解),让我们一看就明白,哦~,这个name
是一个string
类型。
let name = "前端娱乐圈"; // 是一个string类型 let age = 38; // 是一个number类型 let hobby = ["write code", "玩游戏"] // 是一个string数组类型
推导类型就是去掉显示注解,系统自动会识别当前值是一个什么类型的。
六、安装TypeScript && 运行
typescript
全局安装typescript
环境。
npm i -g typescript
可是这只是安装了typescript
,那我们怎么运行.ts
文件呢,安装完typescript
我们就可以执行tsc
命令。
如:我们的文件叫做index.ts
,直接在命令行执行tsc index.ts
即可。然后就可以看到在目录下编译出来一个index.js
,这就是tsc
编译完的结果。
index.ts
const userName: string = "前端娱乐圈"
运行tsc index.ts
,你可以看见在index.ts
的同级下又生成一个index.js
,如下就是编译的结果文件index.js
。
var userName = "前端娱乐圈"
上面我们知道了运行tsc
命令就可以编译生成一个文件,有的小伙伴觉得这样太麻烦了,每次运行只是编译出来一个文件还不是运行,还得用node index.js
才可以运行。不急我们接着往下看
ts-node
我们来看一下这个插件ts-node
,这个插件可以直接运行.ts
文件,并且也不会编译出来.js
文件。
npm i ts-node // 运行 ts-node index.ts
讲到这里我们了解了为什么要用TypeScript和它的优缺点以及它的运行工作方式。
那么接下来步入TypeScript
基础知识的海洋啦~,follow me。
感觉有帮助的小伙伴可以关注一下:前端娱乐圈 公众号,谢谢啦~,每天更新一篇小技巧
七、基础知识
1. 基础静态类型
在TypeScript
中基础类型跟我们JavScript
中基础类型是一样的。只是有各别是Ts
里面新出的。
1. number
const count: number = 18; // 显示注解一个number类型 const count1 = 18; // 不显示注解,ts会自动推导出来类型
2. string
const str: string = "前端娱乐圈"; // 显示注解一个string类型 const str1 = "蛙人"; // 不显示注解,ts会自动推导出来类型
3. boolean
const status: string = false; // 显示注解一个string类型 const status1 = true; // 不显示注解,ts会自动推导出来类型
4. null
const value: null = null; const value: null = undefined; // 这一点null类型可以赋值undefined跟在 js中是一样的,null == undefined
5. undefined
const value: undefined = undefined; const value: undefined = null; // 这一点null类型可以赋值undefined跟在 js中是一样的,null == undefined
6. void
估计到这有一些小伙伴可能对void
这个比较陌生,以为只有TypeScript
才有的。其实不是哈,在我们JavaScript
就已经存在void
关键字啦,它的意思就是无效的,有的小伙伴可能看见过早些项目里面<a href="javascript: void(0)">
这是控制a
标签的跳转默认行为。你不管怎么执行void
方法它都是返回undefined
那么在我们TypeScript
中void
类型是什么呢。它也是代表无效的,一般只用在函数上,告诉别人这个函数没有返回值。
function fn(): void {} // 正确 function testFn(): void { return 1; // 报错,不接受返回值存在 } function fn1(): void { return undefined} // 显示返回undefined类型,也是可以的 function fn2(): void { return null} // 显示返回null类型也可以,因为 null == undefined
7. never
never
一个永远不会有值的类型或者也可以说一个永远也执行不完的类型,代表用于不会有值,undefined
、null
也算做是值。一般这个类型就不会用到,也不用。大家知道这个类型就行。
const test: never = null; // 错误 const test1: never = undefined // 错误 function Person(): never { // 正确,因为死循环了,一直执行不完 while(true) {} } function Person(): never { // 正确,因为递归,永远没有出口 Person() } function Person(): never { // 正确 代码报错了,执行不下去 throw new Error() }
8. any
any
这个类型代表任何的、任意的。希望大家在项目中,不要大片定义any
类型。虽然它真的好使,那这样我们写TypeScript
就没有任何意义了。
let value: any = ""; // 正确 value = null // 正确 value = {} // 正确 value = undefined // 正确
9. unknown
unknown
类型是我们TypeScript
中第二个any
类型,也是接受任意的类型的值。它的英文翻译过来就是未知的,我们来看一下栗子
let value: unknown = "" value = 1; value = "fdsfs" value = null value = {}
那现在肯定有小伙伴疑惑,诶,那它unknown
相当于是any
类型,那二者的区别是什么。我们来看一下
let valueAny: any = ""; let valueUnknown: unknown = ""; valueAny = "蛙人"; valueUnknown = "前端娱乐圈" let status: null = false; status = valueAny; // 正确 status = valueUnknown // 报错,不能将unknown类型分配给null类型
我们来看一下上面的,为什么any
类型就能被赋值成功,而unknown
类型不行呢,从它俩的意义来上看,还是有点区别的,any
任何的,任意的、unknown
未知的。所以你给unknown
类型赋值任何类型都没关系,因为它本来就是未知类型嘛。但是你如果把它的unknown
类型去被赋值一个null
类型,这时人家null
这边不干了,我不接受unknown
类型。
说白了一句话,别人不接受unknown
类型,而unknown
类型接受别人,哈哈哈哈。
2. 对象静态类型
说起对象类型,我们肯定都能想到对象包含{}
、数组
、类
、函数
1. object && {}
其实这俩意思一样,{}
、object
表示非原始类型,也就是除number
,string
,boolean
,symbol
,null
或undefined
之外的类型。
const list: object = {} // 空对象 const list1: object = null; // null对象 const list: object = [] // 数组对象 const list: {} = {} list.name = 1 // 报错 不可更改里面的字段,但是可以读取 list.toString()
const list: [] = []; // 定义一个数组类型 const list1: number[] = [1,2] // 定义一个数组,里面值必须是number const list2: object[] = [null, {}, []] // 定义一个数组里面必须是对象类型的 const list3: Array<number> = [1,2,3] // 泛型定义数组必须是number类型,泛型我们待会讲到
// 类 class ClassPerson = { name: "前端娱乐圈" } const person: ClassPerson = new Person(); person.xxx = 123; // 这行代码报错,因为当前类中不存在该xxx属性
// 函数 const fn: () => string = () => "前端娱乐圈" // 定义一个变量必须是函数类型的,返回值必须是string类型
3. 函数类型注解
这里说一下函数显示注解和函数参数不会类型推导问题。
1. 函数返回类型为number
function fn(a, b): number { return a + b; } fn(1, 2)
2. 函数void
显示注解为void
类型,函数没有返回值。
function fn(): void { console.log(1) }
3. 函数不会自动类型推导
可以看到下面的函数类型,不会自动类型推导,我们实参虽然传入的1和2,但是形参方面是可以接受任意类型值的,所以系统也识别不出来你传递的什么,所以这里得需要我们显示定义注解类型。
function testFnQ(a, b) { return a + b } testFnQ(1,2)
我们来改造一下。
function testFnQ(a:number, b:number) { return a + b } testFnQ(1,2)
我们再来看一下参数对象显示注解类型,也是在:
号后面赋值每个字段类型即可。
function testFnQ(obj : {num: number}) { return obj.num } testFnQ({num: 18})
4. 元组Tuple
元组用于表示一个已知数组的数量和类型的数组,定义数组中每一个值的类型,一般不经常使用。
const arr: [string, number] = ["前端娱乐圈", 1] const arr: [string, string] = ["前端娱乐圈", 1] // 报错
5. 枚举Enum
Enum
枚举类型,可以设置默认值,如果不设置则为索引。
enum color { RED, BLUE = "blue", GREEN = "green" } // color["RED"] 0 // color["BLUE"] blue
像上面的color
中RED没
有设置值,那么它的值则为0
,如果BLUE
也不设置的话那么它的值则是1
,它们这里是递增。如果设置值则是返回设置的值
注意这里还有一个问题,直接来上代码
通过上面学习我们知道了enum
可以递增值,也可以设置默认值。但是有一点得注意一下,enum
没有json
对象那样灵活,enum
不能在任意字段上设置默认值。
比如下面栗子,RED
没有设置值,然后BLUE
设置了默认值,但是GREEN
又没有设置,这时这个GREEN
会报错。因为你第二个BLUE
设置完默认值,第三又不设置,这时代码都不知道该咋递增了,所以报错。还有一种方案就是你给BLUE
可以设置一个数字值,这时第三个GREEN
不设置也会跟着递增,因为都是number
类型。
// 报错 enum color { RED, BLUE = "blue", GREEN } // good enum color { RED, // 0 BLUE = 4, // 4 GREEN // 5 }
比如enum
枚举类型还可以反差,通过value
查key
值。像我们json
对象就是不支持这种写法的。
enum color { RED, // 0 BLUE = 4, // 4 GREEN // 5 } console.log(color[4]) // BLUE console.log(color[0]) // RED
5. 接口Interface
接口interface
是什么,接口interface
就是方便我们定义一处代码,多处复用。接口里面也存在一些修饰符。下面我们来认识一下它们吧。
1. 接口怎么复用
比如在讲到这之前,我们不知道接口
这东西,可能需要给对象定义一个类型的话,你可能会这样做。
const testObj: { name: string, age: number } = { name: "前端娱乐圈", age: 18 } const testObj1: { name: string, age: number } = { name: "蛙人", age: 18 }
我们用接口来改造一下。
interface Types { name: string, age: number } const testObj: Types = { name: "前端娱乐圈", age: 18 } const testObj1: Types = { name: "蛙人", age: 18 }
可以看到使用interface
关键字定义一个接口,然后赋值给这两个变量,实现复用。
2. readonly修饰符
readonly
类型,只可读状态,不可更改。
interface Types { readonly name: string, readonly age: number } const testObj: Types = { name: "前端娱乐圈", age: 18 } const testObj1: Types = { name: "蛙人", age: 18 } testObj.name = "张三" // 无法更改name属性,因为它是只读属性 testObj1.name = "李四" // 无法更改name属性,因为它是只读属性
3. ?可选修饰符
可选修饰符以?
定义,为什么需要可选修饰符呢,因为如果我们不写可选修饰符
,那interface
里面的属性都是必填的。
interface Types { readonly name: string, readonly age: number, sex?: string } const testObj: Types = { name: "前端娱乐圈", age: 18}
4. extends继承
我们的interface
也是可以继承的,跟ES6Class
类一样,使用extends
关键字。
interface Types { readonly name: string, readonly age: number, sex?: string } interface ChildrenType extends Types { // 这ChildrenType接口就已经继承了父级Types接口 hobby: [] } const testObj: ChildrenType = { name: "前端娱乐圈", age: 18, hobby: ["code", "羽毛球"] }
5. propName扩展
interface里面这个功能就很强大,它可以写入不在interface里面的属性。
interface Types { readonly name: string, readonly age: number, sex?: string, } const testObj: Types = { name: "前端娱乐圈", age: 19, hobby: [] }
上面这个testObj
这行代码会爆红,因为hobby
属性不存在interface
接口中,那么我们不存在的接口中的,还不让人家写了?。这时候可以使用自定义就是上面的propName
。
interface Types { readonly name: string, readonly age: number, sex?: string, [propName: string]: any // propName字段必须是 string类型 or number类型。 值是any类型,也就是任意的 } const testObj: Types = { name: "前端娱乐圈", age: 19, hobby: [] }
在运行上面代码,就可以看到不爆红了~
6. Type
我们再来看一下Type
,这个是声明类型别名使的,别名类型只能定义是:基础静态类型
、对象静态类型
、元组
、联合类型
。
注意:type别名不可以定义interface
type Types = string; type TypeUnite = string | number const name: typeUnite = "前端娱乐圈" const age: typeUnite = 18
1. 那么type类型别名和interface接口有什么区别呢
1. type不支持interface声明
type Types = number type Types = string // 报错, 类型别名type不允许出现重复名字 interface Types1 { name: string } interface Types1 { age: number } // interface接口可以出现重复类型名称,如果重复出现则是,合并起来也就是变成 { name:string, age: number }
第一个Types
类型别名type不允许出现重复名字,interface接口可以出现重复类型名称,如果重复出现则是,合并起来也就是变 { name:string, age: number }
再来看一下interface另一种情况
interface Types1 { name: string } interface Types1 { name: number }
可以看到上面两个同名称的interface
接口,里面的属性也是同名称,但是类型不一样。这第二个的Types1
就会爆红,提示:后续声明的接口,必须跟前面声明的同名属性类型必须保持一致,把后续声明的name
它类型换成string
即可。
2. type支持表达式 interface不支持
const count: number = 123 type testType = typeof count const count: number = 123 interface testType { [name: typeof count]: any // 报错 }
可以看到上面type
支持表达式,而interface
不支持
3. type 支持类型映射,interface不支持
type keys = "name" | "age" type KeysObj = { [propName in keys]: string } const PersonObj: KeysObj = { // 正常运行 name: "蛙人", age: "18" } interface testType { [propName in keys]: string // 报错 }
7. 联合类型
联合类型
用|
表示,说白了就是满足其中的一个类型
就可以。
const statusTest: string | number = "前端娱乐圈" const flag: boolean | number = true
再来看一下栗子。我们用函数参数使用联合类型看看会发生什么
function testStatusFn(params: number | string) { console.log(params.toFixed()) // 报错 } testStatusFn(1)
上面我们说过了,函数参数类型不能类型自动推导,更何况现在用上联合类型,系统更懵逼了,不能识别当前实参的类型。所以访问当前类型上的方法报错。
接下来带大家看一些类型保护
,听着挺高级,其实这些大家都见过。别忘了记得关注:前端娱乐圈 公众号哦,嘻嘻
1. typeof
function testStatusFn(params: number | string) { console.log(params.toFixed()) // 报错 } testStatusFn(1)
改造后
// 正常 function testStatusFn(params: string | number) { if (typeof params == "string") { console.log(params.split) } if (typeof params == "number") { console.log(params.toFixed) } } testStatusFn(1)
// 报错 interface frontEnd { name: string } interface backEnd { age: string } function testStatusFn(params: frontEnd | backEnd) { console.log(params.name) } testStatusFn({name: "蛙人"})
改造后
// 正常 function testStatusFn(params: frontEnd | backEnd) { if ("name" in params) { console.log(params.name) } if ("age" in params) { console.log(params.age) } } testStatusFn({name: "蛙人"})
3. as 断言
// 报错 interface frontEnd { name: string } interface backEnd { age: string } function testStatusFn(params: frontEnd | backEnd) { console.log(params.name) } testStatusFn({name: "蛙人"})
改造后
// 正常 function testStatusFn(params: frontEnd | backEnd) { if ("name" in params) { const res = (params as frontEnd).name console.log(res) } if ("age" in params) { const res = (params as backEnd).age console.log(res) } } testStatusFn({age: 118})
8. 交叉类型
交叉类型
就是跟联合类型相反,它用&
表示,交叉类型
就是两个类型必须存在。这里还用上面的联合类型的栗子来看下。
interface frontEnd { name: string } interface backEnd { age: number } function testStatusFn(params: frontEnd & backEnd) {} testStatusFn({age: 118, name: "前端娱乐圈"})
这里我们可以看到实参必须传入两个接口(interface)全部的属性值才可以。联合类型是传入其中类型就可以。
注意:我们的接口interface出现同名属性
interface frontEnd { name: string } interface backEnd { name: number } function testStatusFn(params: frontEnd & backEnd) { console.log(params) } testStatusFn({name: "前端"})
上面我们两个接口类型中都出现了同名属性,但是类型不一样,这时类型就会变为never
。
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/51e337707b6641e590bbb0139fabacdb~tplv-k3u1fbpfcp-watermark.image" width="90%">
泛型是TypeScript
中最难理解的了,这里我尽量用通俗易懂的方式讲明白。
function test(a: string | number, b: string | number) { console.log(a, b) } test(1, "前端娱乐圈")
比如上面栗子,函数参数注解类型
定义string
和number
,调用函数实参传入也没什么问题,但是有个需求,就是实参我们必须传入同样的类型(传入两个number
类型)。虽然上面这种联合类型也可以实现,但是如果我们要在加一个boolean
类型,那么联合类型还得在追加一个boolean
,那这样代码太冗余了。
这时就需要用到泛型了,泛型是专门针对不确定的类型使用,并且灵活。泛型的使用大部分都是使用<T>
,当然也可以随便使用,如:<Test>
、<Custom>
都可以。
function test<T>(a: T, b: T) { console.log(a, b) } test<number>(1, "前端娱乐圈") // 调用后面跟着尖括号这就是泛型的类型,这时报错,因为在调用的使用类型是number,所以只能传入相同类型的 test<boolean>(true, false) test<string>("前端娱乐圈", "蛙人")
上面这使用泛型就解决了我们刚才说的传入同一个类型参数问题,但是泛型也可以使用不同的参数,可以把调用类型定义为<any>
function test<T>(a: T, b: T) { console.log(a, b) } test<any>(1, "前端娱乐圈")
但是上面这种又有一种问题,它可以传入对象,但是如果我们只希望传入number
类型和string
类型。那么我们泛型也给我们提供了约束
类型。泛型使用extends
进行了类型约束,只能选择string
、number
类型。
function test<T extends number | string, Y extends number | string>(a: T, b: Y) { console.log(a, b) } test<number, string>(18, "前端娱乐圈") test<string, number>("前端娱乐圈", 18)
这时,传入泛型时使用,
逗号分隔,来定义每一个类型希望是什么。记住,只有我们不确定的类型,可以使用泛型。
10. 模块
TypeScript
也支持import
和export
这里大多数小伙伴都知道,这里都不多讲啦。
// 导入 import xxx, { xxx } from "./xxx" // 导出 export default {} export const name = "前端娱乐圈"
如有不明白的小伙伴,可以看我以前文章 聊聊什么是CommonJs和Es Module及它们的区别
11. Class类
以下这三个修饰符是在
TypeScript
类中才能使用,在JavaScript
类中是不支持的。
1. public
public
为类
的公共属性,就是不管在类
的内部还是外部,都可以访问该类
中属性及方法。默认定义的属性及方法都是public
。
class Person { name = "前端娱乐圈"; public age = 18; } const res = new Person(); console.log(res.name, res.age) // 前端娱乐圈 18
上面可以看到打印结果都能显示出来,name
属性没有定义public
公共属性,所以类
里面定义的属性及方法默认都是public
定义。
2. private
private
为类
的私有属性,只有在当前类
里面才能访问,当前类
就是{}
里面区域内。在{}
外面是不能访问private
定义的属性及方法的
class Person { private name = "前端娱乐圈"; private age = 18; } const res = new Person(); console.log(res.name, res.age) // 这俩行会爆红,当前属性为私有属性,只能在类内部访问 class Scholl extends Person { getData() { return this.username + "," + this.age } } const temp = new Scholl() console.log(temp.getData()) // 爆红~,虽然继承了Person类,但是private定义是只能在当前类访问,子类也不能访问。
3. protected
protected
为类
的保护属性,只有在当前类和子类可以访问。也就是说用protected
属性定义的子类也可以访问。
class Person { protected username = "前端娱乐圈"; protected age = 18; } const res = new Person(); console.log(res.name, res.age) // 这俩行会爆红,当前属性为私有属性,只能在类内部访问 class Scholl extends Person { getData() { return this.username + "," + this.age } } const temp = new Scholl() console.log(temp.getData()) // 前端娱乐圈,18。可以正常访问父类的属性
4. implements
implements
关键字只能在class
中使用,顾名思义,实现一个新的类,从父级或者从接口实现所有的属性和方法,如果在PersonAll
类里面不写进去接口里面已有的属性和方法则会报错。
interface frontEnd { name: string, fn: () => void } class PersonAll implements frontEnd { name: "前端娱乐圈"; fn() { } }
5. 抽象类
抽象类使用abstract
关键字定义。abstract
抽象方法不能实例化,如果,抽象类里面方法是抽象的,那么本身的类也必须是抽象的,抽象方法不能写函数体。父类里面有抽象方法,那么子类也必须要重新该方法。
// 抽象类 abstract class Boss { name = "秦"; call() {} // 抽象方法不能写函数体 } class A extends Boss { call() { console.log(this.name); console.log("A") } } class B extends Boss { call() { console.log("B") } } new A().call()
该抽象类使用场景,比如A
需求或者B
需求正好需要一个公共属性,然后本身还有一些自己的逻辑,就可以使用抽象类,抽象类只能在TypeScript
中使用。
12. 命名空间namespace
我们学到现在可以看到,不知道小伙伴们发现没有,项目中文件是不是不能有重复的变量(不管你是不是一样的文件还是其它文件),否则就直接爆红了。命名空间一个最明确的目的就是解决重名问题。
命名空间使用namespace
关键字来定义,来看栗子吧。
index.ts
namespace SomeNameSpaceName { const q = {} export interface obj { name: string } }
上面这样,就定义好了一个命名空间,可以看到变量q
没有写export
关键字,这证明它是内部的变量,就算别的.ts
文件引用它,它也不会暴露出去。而interface
这个obj
接口是可以被全局访问的。
我们在别的页面访问当前命名空间
1. reference引入
/// <reference path="./index.ts" /> namespace SomeNameSpaceName { export class person implements obj { name: "前端娱乐圈" } }
2. import
export interface valueData { name: string }
import { valueData } from "./xxx.ts"
这时使用命名空间之后完全可以解决不同文件重名爆红问题。
13. tsConfig.json
这个tsconfig
文件,是我们编译ts文件,如何将ts
文件编译成我们的js
文件。tsc -init
这个命令会生成该文件出来哈。执行完该命令,我们可以看到根目录下会生成一个tsconfig.json
文件,里面有一堆属性。
那么我们怎么将ts
文件编译成js
文件呢,直接执行tsc
命令可以将根目录下所有的.ts
文件全部编译成.js
文件输出到项目下。
更多配置文档,请参考官网
{ // include: ["*.ts"] // 执行目录下所有的ts文件转换成js文件 // include: ["index.ts"] // 只将项目下index.ts文件转换为js文件 // files: ["index.ts"] // 跟include一样,只执行当前数组值里面的文件,当前files必须写相对路径 // exclude: ["index.ts"] // exclude就是除了index.ts不执行,其它都执行 compilerOptions: { removeComments: true, // 去掉编译完js文件的注释 outDir: "./build", // 最终输出的js文件目录 rootDir: "./src", // ts入口文件查找 } }
八、实用类型
最后来说一下实用类型,TypeScript
标准库自带了一些实用类型。这些实用类都是方便接口Interface
使用。这里只列举几个常用的,更多实用类型官网
1. Exclude
从一个类型中排除另一个类型,只能是联合类型,从TypesTest
类型中排除UtilityLast
类型。
适用于:并集类型
interface UtilityFirst { name: string } interface UtilityLast { age: number } type TypesTest = UtilityFirst | UtilityLast; const ObjJson: Exclude<TypesTest, UtilityLast> = { name: "前端娱乐圈" }
2. Extract
Extract
正好跟上面那个相反,这是选择某一个可赋值的联合类型,从TypesTest
类型中只选择UtilityLast
类型。
适用于:并集类型
interface UtilityFirst { name: string } interface UtilityLast { age: number } type TypesTest = UtilityFirst | UtilityLast; const ObjJson: Extract<TypesTest, UtilityLast> = { age: 1 }
3. Readonly
把数组或对象的所有属性值转换为只读的。这里只演示一下对象栗子,数组同样的写法。
适用于:对象、数组
interface UtilityFirst { name: string } const ObjJson: Readonly<UtilityFirst> = { name: "前端娱乐圈" } ObjJson.name = "蛙人" // 报错 只读状态
4. Partial
把对象的所有属性设置为可选的。我们知道interface
只要不设置?
修饰符,那么对象都是必选的。这个实用类可以将属性全部转换为可选的。
适用于:对象
interface UtilityFirst { name: string } const ObjJson: Partial<UtilityFirst> = { }
5. Pick
Pick
选择对象类型中的部分key
值,提取出来。第一个参数目标值
,第二个参数联合key
适用于:对象
interface UtilityFirst { name: string, age: number, hobby: [] } const ObjJson: Pick<UtilityFirst, "name" | "age"> = { name: "前端娱乐圈", age: 18 }
6. Omit
Omit
选择对象类型中的部分key
值,过滤掉。第一个参数目标值
,第二个参数联合key
适用于:对象
interface UtilityFirst { name: string, age: number, hobby: string[] } const ObjJson: Omit<UtilityFirst, "name" | "age"> = { hobby: ["code", "羽毛球"] }
7. Required
Required
把对象所有可选属性转换成必选属性。
适用于:对象
interface UtilityFirst { name?: string, age?: number, hobby?: string[] } const ObjJson: Required<UtilityFirst> = { name: "蛙人", age: 18, hobby: ["code"] }
8. Record
创建一个对象结果集,第一个参数则是key
值,第二个参数则是value
值。规定我们只能创建这里面字段值。
适用于:对象
type IndexList = 0 | 1 | 2 const ObjJson: Record<IndexList, "前端娱乐圈"> = { 0: "前端娱乐圈", 1: "前端娱乐圈", 2: "前端娱乐圈" }
谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。
我是蛙人,如果觉得写得可以的话,请点个赞吧❤。
感兴趣的小伙伴可以加入 [ 前端娱乐圈交流群 ] 欢迎大家一起来交流讨论
写作不易,「点赞」+「在看」+「转发」 谢谢支持❤
下篇更新TypeScript实战,欢迎关注
Recommend
-
171
总监最看重的!写给初级UE/UI设计师的面试经验 我要投稿 推荐:
-
49
作为一个优秀的程序员,不仅仅可以解决工作的问题,还要不断的关注前端技术的发展,其中也包括了解最新的前端面试题,那么知识点来了,请接好 1. 你熟悉的es6 let、const、var的使用区别let: 相当于var,用于声明一个变量,在块级作用域有效(可
-
61
最近到了金三银四的跳槽季,很多人都会面临跳槽找工作,并且再过几个月又会到毕业季,越来越多的毕业生会面临这个问题。 同样,我们组因为业务需要(我们今年倒是还没有人员离职,感动╭(╯^╰)╮)需要进行社招,我最近看了不少简历,并且对一些小伙伴进行了电话沟通...
-
22
目录 前言 概念 js基础知识 Document基础知识 网络 CSS 前言 常常有人问我,如何成为一名初、中、高级前端。并且发现我身边的大多数人知识面并不是很成体系,总会有各种知识漏洞。如果你还不清楚如何才算初级工程师,自己的知识体系有没有漏洞,可以参考
-
27
写这篇文章的初衷,是因为看到很多刚入职同学的迷茫、没有目标感,看到了一些工作 1-2 年的同学疲于业务开发而没有很大的成长, 也看到了一些同学 2~3 年工作时间变成了框架熟练的“打工人”,却没有很好的沉淀和亮点,导致无法进入大厂,实现薪资的大幅提升,于...
-
13
【爆肝一周】面试10多家中大厂后的万字总结——❤️JavaWeb篇❤️(建议收藏) ...
-
11
1 环境安装 学习python开发,首先要学会安装Python环境,我一般使用Anaconda+Pycharm作为开发环境。Anaconda是一个开源的Python发行版本,其包含了
-
8
本文收录于《技术专家修炼》 文中配套资料合集 提取...
-
7
1024,一封写给CSDN家园Python初学者的信 | Python初级、中级、高级学习路线 ...
-
14
写给初级程序员的十点提升建议 1 遇到产线问题,不管是不是自己管的,都去关注。哪怕本次无法解决问题,其...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK