因为在春节期间,实在没什么事干,就想系统的写份typescript
笔记。废话很少说,为何咱们都在说着ts
这件事呢,尤为对于前端开发者来讲,JavaScript
做为行为交互,是四剑客之一。那么为何还要用ts
呢?javascript
咱们知道,目前国内主流的3大前端开发框架:html
框架名称 | version | github star |
---|---|---|
React | V16.12.0 | 142,715 |
Vue.js | V2.x | 155,969 |
Angular | 56,653 |
tips:这里暂不说跨平台解决方案,好比比较优秀的Flutter
等。 固然了咱们会有一个疑问,假若咱们用ts
编码,会不会现有的框架会不支持,这点我们大可没必要担忧。前端
那咱们想再看下,当前比较流行的框架、库,它们的源码状况vue
正是因为java
ts
的比重愈来愈多ts
重构ts
以及咱们众所周知的JavaScript
自己存在的一些不够完美的地方,例如xxx 未定义
,等等。固然了在一些原有的项目中可能也不习惯从新接触ts
,那么我和你们一块来来作作笔记,这对于咱们仍是颇有必要的。node
编辑器 这里推荐很好用的Vscode
,本笔记相关代码也是用此react
相关环境版本git
第三方辅助github
npm install -g ts-node
复制代码
具体使用参考 ts-node xxx.tstypescript
npm i -g nodemon
复制代码
看过我其余笔记的掘友,知道我是刚搞了台显示器
,那电脑仍是用的比较陈旧的不灵敏的mac
,因此可能会少些xmind 图
什么的,但愿您也能看下去,这么无聊的文字。笔记主要分为三部分
首先,咱们不论是读什么文章,或者茶余饭后同事唠嗑,多少都有耳闻,ts
相比于js
会有几点不一样
对这几点对于编程来讲仍是尤其重要的,咱们先看几个简单的demo
一些简单的浏览器调试,我们选择在vscode
里直接预览,这里推荐一个插件
具体的使用方法可参考文末
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()
复制代码
本节小结
能够简单的体会到如下几点
/** * * @param {string} name * @param {number} age * @param {string} sex */
const Me = (name, age, sex) => `个人名字是${name},我今年${age},我是${sex}生,欢迎关注个人掘金呦~`
console.log(Me(`洋小洋同窗`, 18, `男`))
=>个人名字是洋小洋同窗,我今年18,我是男生,欢迎关注个人掘金呦~
复制代码
老规矩,我们一块写个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代码
复制代码
这里咱们能够经过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
private
protected
其实在类的内部,全部的属性
和方法
默认是经过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
复制代码
当被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
等常见的方法在其余的编程语言中,获取属性与设置也一样的有这种状况 在前面部分咱们提到过
这样的话,就遇到一个问题,没法读取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)
复制代码
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
中的元素
能够经过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 ,也欢迎指正不恰当的地方,感谢~