写在前面
由于在春节期间,实在没什么事干,就想系统的写份typescript
笔记。废话不多说,为什么我们都在说着ts
这件事呢,尤其对于前端开发者来说,JavaScript
作为行为交互,是四剑客之一。那么为什么还要用ts
呢?
现状分析
我们知道,目前国内主流的3大前端开发框架:
框架名称 | version | github star |
---|---|---|
React | V16.12.0 | 142,715 |
Vue.js | V2.x | 155,969 |
Angular | 56,653 |
tips:这里暂不说跨平台解决方案,比如比较优秀的Flutter
等。
当然了我们会有一个疑问,倘若我们用ts
编码,会不会现有的框架会不支持,这点咱们大可不必担心。
那我们想再看下,当前比较流行的框架、库,它们的源码情况
- Redux
- mobx
正是由于
- 优秀的第三方框架库
ts
的比重越来越多 - 即将发布的Vue.js 3x 版本,源码用
ts
重构 - 老旧的项目可以无缝衔接
ts
以及我们众所周知的JavaScript
本身存在的一些不够完美的地方,例如xxx 未定义
,等等。当然了在一些原有的项目中可能也不习惯重新接触ts
,那么我和大家一块来来做做笔记,这对于我们还是很有必要的。
环境准备
-
编辑器 这里推荐很好用的
Vscode
,本笔记相关代码也是用此 -
相关环境版本
- node
- npm
- vue/cli
-
第三方辅助
-
- ts-node@8.6.2 TypeScript execution and REPL for node.js
npm install -g ts-node 复制代码
具体使用参考 ts-node xxx.ts
-
-
- nodemon@2.0.2
npm i -g nodemon 复制代码
笔记大纲
看过我其他笔记的掘友,知道我是刚搞了台显示器
,那电脑还是用的比较陈旧的不灵敏的mac
,所以可能会少些xmind 图
什么的,希望您也能看下去,这么无聊的文字。笔记主要分为三部分
前期思路
JavaScript vs TypeScript
首先,我们不管是读什么文章,或者茶余饭后同事唠嗑,多少都有耳闻,ts
相比于js
会有几点不同
- 真正意义上的面向对象编程
- 类型校验
- 是js 的超集
- ...
对这几点对于编程来说还是尤为重要的,我们先看几个简单的demo
一些简单的浏览器调试,咱们选择在vscode
里直接预览,这里推荐一个插件
- Brower Previews
具体的使用方法可参考文末
- 声明一个变量,并赋值
aString
是一个字符串,可又能重新赋值为数字
- 定义一个求和功能函数
let aString = `我是一个字符串`
aString = 123
// alert(aString)
console.log(aString)
const addFn = (num1, num2) => alert(num1 + num2)
console.log(1, 2)
console.log('1', '2')
console.log('1', 2)
console.log(1)
复制代码
我们可以发现,运行的时候才知道我们出现了参数的问题,那么这何尝不是我们头疼的一个问题呢,就像是
undefined.fliter()
复制代码
本节小结
可以简单的体会到以下几点
- 始于js,归于js
- 弱类型渐渐有点强
- 社区更新的非常快
- 适合大型项目的开发
- 更好尽早的避免BUG
- 能够少些一些注释 例如我们的一些方法,可以减少相关的文档注释
/**
*
* @param {string} name
* @param {number} age
* @param {string} sex
*/
const Me = (name, age, sex) => `我的名字是${name},我今年${age},我是${sex}生,欢迎关注我的掘金呦~`
console.log(Me(`洋小洋同学`, 18, `男`))
=>我的名字是洋小洋同学,我今年18,我是男生,欢迎关注我的掘金呦~
复制代码
- IDE良好的提示(这里我们后续慢慢体会下)
学习文档
hello-world
老规矩,咱们一块写个hello world
const hello = (): string => `hello TS`
复制代码
我们知道浏览器是不支持ts
代码的(目前),那么我们该如何编辑一下呢
安装
> npm install -g typescript
复制代码
+ typescript@3.7.5
这样我们就可以通过tsc xxx.ts
来进行编译为js
代码
var hello = function () { return "hello TS"; }; // js代码
复制代码
watch 监听
这里我们可以通过tsc -w xx.ts
来监听ts
文件的变化并编译
遇到问题
在我们基础调试的过程中,会遇到 was also declared here
类型
这里咱们知道:JS数据类型
分类和判断 JavaScript中有6种数据类型:数字(number)、字符串(string)、布尔值(boolean)、undefined、null、对象(Object)。 其中对象类型包括:数组(Array)、函数(Function)、还有两个特殊的对象:正则(RegExp)和日期(Date)。
那么在ts
中,定义变量
let a: number
a = 123 // 其中a 只可以是number 类型
复制代码
任意类型
在ts
中有一种类型,叫做任意类型any
- 尽可能少的使用
any
let list: any[] = [1, true, "free"];
list[1] = 100;
复制代码
const myInfoFn = (info: any) => {
switch (typeof info) {
case `number`:
console.log(`我今年${info}`)
case `string`:
console.log(`我的名字${info}`)
default:
}
}
myInfoFn(`yayxs`)
复制代码
数组
在实际的开发中,经常打交道的便是数组
,那么在ts
中有两种常用定义数组的方式(其中数组的每项元素是同一类型)
let arrSt: string[] = [`yayxs`] // 每一项是string
let arrNum: Array<number> = [1, 2, 3] // 每一项是number
复制代码
元组
- 元素的个数固定
- 顺序尽量不变
let tuple: [string, number, string]
tuple = [`yayxs`, 18, `男生`]
console.log(`${tuple[0]}是${tuple[2]},今年${tuple[1]}`)
复制代码
函数
函数无非是参数``返回值
等,其中包括默认参数,可选参数等
const addFn = (a: number, b: number): number => a + b
const addCon = (a: number, b: number): void => console.log(a+b) // 函数没有返回值
const addFn = (a: number, b: number = 10): number => a + b
// 参数默认值 b默认值值10
复制代码
可选参数
const addABC = (a: number, b: number, c?: number): number => {
if (c) {
return a + b + c
} else {
return a + b
}
}
复制代码
不确定参数
口语中的不确定参数又叫剩余参数
,示例:
const arrList: Array<number> = [1, 2, 3, 4];
// 其中acc为累加器;cur当前值
const reducer = (acc, cur): number => acc + cur;
let res: number = arrList.reduce(reducer, 5)
console.log(res) // 15
const addFun = (a: number, ...nums: number[]) => nums.reduce(reducer, a)
console.log(addFun(1, 2, 3, 4)) // 10
复制代码
类
class MyGirlFriend {
// 定义数据内容
name: string;
age: number;
height: string
}
// new 对象
let xuehua = new MyGirlFriend()
// set 内容
xuehua.name = `xuehua` // 雪花
// get 内容
console.log(xuehua.name)
复制代码
class MyGirlFriend {
// 定义数据内容
name: string;
age: number;
height: string
constructor(name: string, age: number, height: string) {
this.name = name
this.age = age
this.height = height
}
formatHeight() {
return `${this.height} cm`
}
}
// new 对象
let xuehua = new MyGirlFriend(`xuehua`, 20, `172`)
console.log(xuehua.name) // xuehua
console.log(xuehua.age) // 20
console.log(xuehua.formatHeight()) // 172cm
复制代码
我们可以看下编译为js
之后的代码
var MyGirlFriend = /** @class */ (function () {
function MyGirlFriend(name, age, height) {
this.name = name;
this.age = age;
this.height = height;
}
MyGirlFriend.prototype.formatHeight = function () {
return this.height + " cm";
};
return MyGirlFriend;
}());
// new 对象
var xuehua = new MyGirlFriend("xuehua", 20, "172");
console.log(xuehua.name);
console.log(xuehua.age);
console.log(xuehua.formatHeight());
复制代码
继承
我们都知道面向对象``继承``多态
,那么什么是继承嘞:
/// 继承Demo
class Person {
// 数据-属性
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
// 行为-方法
sayHi() {
console.log(`HI`)
}
}
// 继承Person
class Programmer extends Person {
sayNo() {
console.log(`NO`)
}
sayHi() {
// console.log(`en~~~`)
super.sayHi() // 调用父类
}
}
let p = new Programmer(`yayxs`, 18)
p.sayHi()
复制代码
相应的js
代码
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/// 继承Demo
var Person = /** @class */ (function () {
function Person(name, age) {
this.name = name;
this.age = age;
}
// 行为-方法
Person.prototype.sayHi = function () {
console.log("HI");
};
return Person;
}());
// 继承Person
var Programmer = /** @class */ (function (_super) {
__extends(Programmer, _super);
function Programmer() {
return _super !== null && _super.apply(this, arguments) || this;
}
Programmer.prototype.sayNo = function () {
console.log("NO");
};
Programmer.prototype.sayHi = function () {
// console.log(`en~~~`)
_super.prototype.sayHi.call(this);
};
return Programmer;
}(Person));
var p = new Programmer("yayxs", 18);
p.sayHi();
复制代码
抽象类
抽象类不能够
被实例化,但是可以被继承,通常要完成方法的实例化
// 抽象类
abstract class Person {
name: string
constructor(name: string) {
this.name = name
}
sayHi(name: string): void {
console.log(`hi`)
}
// 抽象方法 没有方法体
abstract work(): void
}
// let p = new Person() err
class Coder extends Person {
sayHi() {
console.log(`12131`)
}
work() {
console.log(`i am working`)
}
}
复制代码
修饰符
-
public
- 默认就是public 修饰
- 公有,外部也可访问
- 继承的对象也能使用
-
private
- 私有的,内部才能访问
-
protected
- 受保护的,权限稍大于 private
其实在类的内部,所有的属性
和方法
默认是通过public
修饰符来进行修饰的
class Person {
public name:string // 默认都是公开的
}
复制代码
关于ts
中的修饰符在其他语言中也是类似的,例如Java
等。
// 公共,私有与受保护的修饰符
class Person {
public name: string
private age: string
public sayName() {
console.log(`my name is ${this.name}`)
}
private sayAge() {
console.log(`my age is ${this.age}`)
}
}
复制代码
那么通过private
修饰的方法,在外部该怎么访问到呢?
getName() {
console.log(this.name)
} // 获取名字
let p = new Person()
p.getName() // undefined
复制代码
getName() {
console.log(this.name)
}
setName(name: string) {
this.name = name
}
let p = new Person()
p.setName(`yayxs`)
p.getName() // yayxs
复制代码
constructor
当被protected
修饰后,是不能new
的,也就是说不能实例化
静态属性&方法
通过static
修饰的属性或者方法,可以直接通过类.xxx
,
class Person {
public static _name: string;
public static getName(): string {
return Person._name
}
}
console.log(Person.getName())
复制代码
本节小结
在ts
中类的实践还是十分有意义的,包括像静态属性方法,或者像public
protected
private
这些修饰符修饰在属性以及方法前边,主要是权限的限制,大致是
public
> protected
> private
具体的修饰细则,包括像
在父类以及子类的调用权限问题,可以翻阅相关的文档
- 静态成员更像是存在于类的本身,而不是
new
出来的实例对象上,这点官网也有说到
枚举
首先不得不提的是枚举在实际开发的过程中,还是十分有益的,能够增强代码的可读性,笔者在封装网络请求类或者网页中常见的下拉选择框有用到
enum RequestType {
GET, POST, PUT, DELETE
}
console.log(RequestType.DELETE) // 3
复制代码
之于为什么是3
呢
var RequestType;
(function (RequestType) {
RequestType[RequestType["GET"] = 0] = "GET";
RequestType[RequestType["POST"] = 1] = "POST";
RequestType[RequestType["PUT"] = 2] = "PUT";
RequestType[RequestType["DELETE"] = 3] = "DELETE";
})(RequestType || (RequestType = {}));
console.log(RequestType.DELETE);
复制代码
接口
接口在实际生活中也是无处不在的,像水龙头与水管
插板与插槽
等等,那么在程序中也是一样的,同一接口规范了一类
// 接口
interface HasName {
name: string
}
// 定义对象
const obj = {
name: `yayxs`
}
// 定义方法
const sayName = (o: HasName) => {
console.log(`my name is ${o.name}`)
}
sayName(obj) // my name is yayxs
复制代码
以上的接口示例是只有参数,那接口里也是可以定义方法的,就像这样
// 接口
interface HasName {
name: string
printName(name: string): void
}
// 定义对象
const obj = {
name: `yayxs`,
printName: (name: string) => {
console.log(name)
}
}
// 定义方法
const sayName = (o: HasName) => {
console.log(`my name is ${o.name}`)
o.printName(o.name) // yayxs
}
sayName(obj)
复制代码
可选参数
在定义接口的时候,我们可以定义一部分可选择的属性或方法,在实现接口的时候不是非要实现
/// 可选参数
type PerInfo = string
interface Person {
name: PerInfo
age?: number
sex: PerInfo
printName(): void
printAge?(): void
}
// 我实现人类接口
class Me implements Person {
printName(): void {
console.log(`xxx`)
}
name: string
sex: string
}
let m = new Me()
复制代码
类型别名
类型别名,顾名思义就是类型的别名
// 类型别名
type Name = string; // 把string别名为Name 更语义化一点
const myName: Name = `yayxs`
console.log(myName)
复制代码
又或者
type User = {
name: string,
age: number,
sex: string
}
const me: User = {
name: `yayxs`,
age: 18,
sex: `nan`
}
console.log(me)
复制代码
那么类型别名
和接口
有点相似
- 类型别名不可以重复定义,而接口是可以的
- 接口常用些
类与接口
// 定义支付接口
interface Pay {
doSomething(): void // 支付接口方法
}
const myPay = () => {
// 实现接口中的pay()
doSomething: () => {
console.log(`实现支付动作`)
}
}
// 定义不同的类来实现接口
class WxPay implements Pay {
doSomething() {
console.log(`我是微信支付`)
}
}
class AlPay implements Pay {
doSomething() {
console.log(`我是支付宝支付`)
}
}
let weixin_pay: Pay = new WxPay()
let ali_pay: Pay = new AlPay()
复制代码
匿名函数接口
在一些接口的内部声明的函数是没有名字的
// 匿名函数接口
interface Person {
(num: number): void
}
let printNum: Person;
printNum = (num: number) => {
console.log(num)
}
复制代码
类型断言
包括像是在dart
语言内,都会有断言存在
- 主要是在编译时期
- 告诉编译器是什么类型
// 类型断言
let x: any = `hello`
let res = (<string>x).substring(0, 1)
console.log(res)
复制代码
在实际开发中书写断言几种常见的方式
interface Person {
name: string
age: number
}
let me = {} as Person
me.name = `yayxs`
me.age = 18
复制代码
let you = <Person>{
name: `xuehua`,
age: 17
}
复制代码
接口继承
类不能继承多个父类,但是可以实现多个已定义的接口
interface Person {
name: string
}
interface Coder {
age: number
}
class Per implements Person, Coder {
name: string
age: number
}
复制代码
可索引类型
// 可索引类型
interface Istr {
[index: string]: string
}
let myStr: Istr;
myStr = {
'name': `yayxs`
}
interface Inum {
[index: number]: string
}
let myNum: Inum
myNum = [`12`]
复制代码
在如上第二个例子中,有点像数组的定义方式,那与数组有什么不同呢
- 可索引类型没有数组的相关方法和属性
length
或者push
等常见的方法
getter && setter
在其他的编程语言中,获取属性与设置也同样的有这种情况 在前面部分我们提到过
这样的话,就遇到一个问题,无法读取name
属性,这时候可以通过内部方法暴露出来,那同样在类中有get
set
// setter and getter
class Person {
private name: string
private age: number
constructor(age: number, name: string) {
this.name = name
this.age = age
}
get getName(): string {
return this.name
}
set setName(name: string) {
this.name = name
}
}
let p = new Person(18, `yayxs`)
// console.log(p.name)
复制代码
函数
函数重载
在js
中是没有函数重载的,那么在ts
中什么是函数重载呢,
- 意思就是先定义方法,但不实现方法体
- 函数的名称可以重名,并不会被覆盖
// 函数重载
function hello(name: string): string
function hello(age: number): number
function hello(params: any): any {
if (typeof params === `string`) {
return params
} else if (params === `number`) {
return params
}
}
复制代码
非空检查
我们可以通过!
来进行非空的检查
let str: string = `yayxs`;
str!.substring(0, 1)
复制代码
never
- 一种情况是,不期待有返回结果
function loop():never{
while(true){
}
}
复制代码
- 一种是内部抛出异常
function err(): never {
throw Error
}
复制代码
泛型
在其他的语言中像JAVA
中有泛型的概念,
// 泛型
function getArr<T>(params: T[]): T[] {
return params
}
console.log(getArr<number>([22, 1]))
class Person<S, N> {
private _name: S
private _age: N
constructor(name: S, age: N) {
this._name = name
this._age = age
}
get name(): S {
return this._name
}
get age(): N {
return this._age
}
}
let p = new Person<string, number>(`yaxs`, 18)
console.log(p.name)
复制代码
在编译的时候请使用
tsc --target es5 demo12.ts
复制代码
模块化
可以通过两种方式导出ts
中的元素
- module default export
- export
tsconfig.json
可以通过tsc --init
来生成这么一个文件
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
复制代码
具体的注释的含义,可以参考官方文档
倾情链接
如果你还没有看过瘾,在19年度
我曾写过一篇很简单的文章,也是基于第三方的环境搭建的一篇极为简单的博客(依赖github、Vuepress)
写在最后
相关代码已经上传至 笔者github ,也欢迎指正不恰当的地方,感谢~