[ts] Advanced: Basic Knowledge
Ref: TypeScript 2022 再入门
准备阶段
NPM for TS
-
npm 配置
支持 ts。
----------------------------------------------- jeffrey@jeffrey-EnlargeDS:.../work$ npm init -y
Wrote to /root/xiaoma_ts/xiaoma_ts_01-27/work/package.json: { "name": "work", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" } jeffrey@jeffrey-EnlargeDS:.../work$ ls package.json
--------------------------------------------------------------------- jeffrey@jeffrey-EnlargeDS:.../work$ npm install --save-dev typescript npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN work@1.0.0 No description npm WARN work@1.0.0 No repository field. + typescript@5.0.4 added 1 package from 1 contributor and audited 1 package in 3.32s found 0 vulnerabilities ╭───────────────────────────────────────────────────────────────╮ │ │ │ New major version of npm available! 6.14.15 → 9.6.6 │ │ Changelog: https://github.com/npm/cli/releases/tag/v9.6.6 │ │ Run npm install -g npm to update! │ │ │ ╰───────────────────────────────────────────────────────────────╯ jeffrey@jeffrey-EnlargeDS:.../work$
-
执行: 编译成 js
ts是开发时可见,运行时不可见。
jeffrey@jeffrey-EnlargeDS:.../work$ vim main.ts jeffrey@jeffrey-EnlargeDS:.../work$ ls main.ts node_modules package.json package-lock.json --------------------------------------------------- jeffrey@jeffrey-EnlargeDS:.../work$ npx tsc main.ts jeffrey@jeffrey-EnlargeDS:.../work$ ls main.js main.ts node_modules package.json package-lock.json
-
-
tsconifg.json 配置文件
-
如果不想写 main.ts 这个路径,那就配置到tsconifg.json中。
命令:$tsc init // 生成默认的配置文件
{ "compilerOptions": { "target": "es6", "module": "commonjs", "rootDir": "./src", "outDir": "./dist", "removeComments": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true } }
命令:$npx tsc(执行),如下图。
Ref: https://www.typescriptlang.org/tsconfig
-
webpack Doc
webpack 是一个模块打包器。它的主要目标是
https://www.youtube.com/playlist?list=PLmOn9nNkQxJGwOhSsQ5H9JTPmiXGmy8Zw
// packages.json
{
"name": "part3",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": { // 之后这里配置 to support: npm build, npm start, 如下例子所示
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack serve --open chrome.exe"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^9.4.2",
"typescript": "^5.0.4",
"webpack": "^5.82.1",
"webpack-cli": "^5.1.1"
}
}
-
-
增强的 "scripts"
-
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "main": "clear && npx tsc && node dist/main.js", "tsmain": "clear && npx ts-node src/main.ts", "serve": "webpack serve --config webpack.config.js" }
如上设置后,可以用ts的方式去执行。
$ npm run tsmain
$ npx ts-node src/main.ts
-
2. 使 webpack 支持 ts编译
// webpack.config.js
const path = require('path') module.exports = { entry: "./src/index.ts" } module.exports = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, 'dist'), filename: "bundle.js" environment: { arrowFunction: false // webpack支持箭头函数不太好,通过此设置搞定。 } }, mode: 'development', module: { rules:[ { // test 制定的是规则生效的文件 test:/\.ts$/, // 要使用的loader use: "ts-loader" // 要排除的文件 exclude: /mode-modules/ } ] } }
-
-
增强
-
因为这里是ts,所以 js的模块引入如下机制,增强后才能支持。
resolve: { extensions: ['.ts', '.js'] }
-
-
继续增强
-
如下的配置文件 ,enable了两个功能。
a) 如果需要自动准备一个 ./dist/index.html:npm i -D html-webpack-plugin
b) 打包时,自动清空dist中过去的文件。
const HTMLWebpackPlugin = require('html-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin') plugins: [ new CleanWebpackPlugin(), new HTMLWebpackPlugin( { // title: "this is one custom title." template: "./src/index.html" }), ]
--> ./dist/bundle.js
-
-
再次增强
-
为了支持旧浏览器 in bundle.js
虽然 tsconfig.json 中可设置;但是 bundle更强大,支持新语法到旧语法,新技术被支持。
首先需要安装这些包:npm i -D @babel/core @babel/preset-env babel-loader core-js
在webpack.config.js中,添加下述定制兼容老的浏览器。
module.exports = { ... ... module: { rules:[ { test:/\.tx(x), use:[ // 配置 babel. { // 指定加载器. loader: "babel-loader", option: { // 设置预定义的环境 presents: [ [ // 指定环境的插件 "@babel/preset-env", // 配置信息 { // 要兼容的目标浏览器 targets: { "chrome":"58", "ie":"11" }, // 基于老版本的api构建的高级语法。 "corejs": "3", // 使用corejs的方式 “usage" 表示按需加载 "useBuiltIns": "usage" } ] ] } }, "ts-loader" ] } ] } }
旧Repo可作参考:https://github.com/komavideo/LearnTypeScript/tree/master
let myAge: number = 25 let myCareer: string = "sys engineer" let isVIP: boolean = true console.log(typeof isVIP) // any类型让编译器忽略类型检测 let mylang:any = "typescript"; console.log("My Language is " + mylang); mylang = 100; console.log("My Language is " + mylang); // 联合类型的使用 let mydata: string | boolean;
// 字典的使用 const mySite = { name: "haha" url: "www..." } console.log(mySite.name) // --> 推荐的方式如下。 const mySite: { name: string, url: string, tags: string[] // <---- 数组的类型定义 } = { name: "haha", url: "www...", tags: ["IT", "AWS"] } for (const item of mySite.tags) { console.log("-", item) } tags.push("new-item")
//------------------------------------------------------------------ // 定义数组
//------------------------------------------------------------------
let data:string[] = ['Java', 'Ruby', 'Kotlin', 'Dart']; console.log(data); console.log(data[0]); console.log(data[3]); console.log(data[4]); // 追加赋值 data[4] = 'PHP'; console.log(data); console.log(data[4]); // 越界赋值(ts支持) data[10] = 'Go'; console.log(data); console.log(data[10]);
// 相当于只定义了key,而没有value enum IAMRole { AdministratorAccess, AmazonDynamoDBFullAccess, AmaonS3FullAccess }
字典的解构
console.log("helo js.") var myobj = { pname: "Curry", age: 33, height: 195, weight: 84 } let { pname, age, weight } = myobj console.log(pname, age, weight)
箭头函数
const add = (x: number, y: number): number => x+y; const main = () => { add(1,2) }
延展操作符
就是一个典型的reduce操作 of array。如下,箭头函数定义了reduce的细节。
const add = (...numbers: number[]) => { return numbers.reduce( (prev, current) => { return prev+current; }, 0) // 0 is init value. }
作为数组中的一个元素。
const mylist = [1, 2, 3]; console.log(mylist) console.log(...mylist) const new_mylist = [...mylist, 4, 5, 6] console.log(new_mylist) // [1,2,3,4,5,6] const new_mylist2 = [mylist, 4, 5, 6] console.log(new_mylist2) // [[1,2,3], 4, 5, 6]
const addXYZ = (...args) => { // args是个数组,这里反倒是“包裹”的效果。 console.log(args) args.forEach(item => console.log(item)); } addXYZ(1,2,3,4,5,6)
OOP
类
// constructor class Site { constructor(pubic name: string, private url: string); sayhelo() { console.log(`Welcome ${this.name}`) console.log("->", this.url) } }
-
只读的变量
// readonly... class Site { constructor( private readonly id: number, public name: string, private url: string ) {} setId(newId: number) { this.id = newId; // <-- error. } }
继承
// super... class DB { constructor( public name: string, public port: number, public dbtype: string, ) { } desc() { console.log(`${this.name}-${this.port}-${this.dbtype}`) } } class RDB extends DB { constructor( public name: string, public port: number, ) { super(name, port, "RDB") } } class NoSqlDB extends DB { constructor( public name: string, public port: number, ) { super(name, port, "NoSql") } } const main = () => { const myDb = new DB("mysql", 3306, "RDB") myDb.desc()
const mySql = new DB("mysql", 3306) mySql.desc()
const mongodb = new DB("mongoDB", 27017) mongodb.desc() } main()
抽象类与接口类
链接:https://www.jianshu.com/p/28e3b4d61945
抽象类适合用来定义某个领域的固有属性,也就是本质,接口适合用来定义某个领域的扩展功能。
当需要为一些类提供公共的实现代码时,应优先考虑抽象类。因为抽象类中的非抽象方法可以被子类继承下来,使实现功能的代码更简单。
当注重代码的扩展性跟可维护性时,应当优先采用接口。
① 接口与实现它的类之间可以不存在任何层次关系,接口可以实现毫不相关类的相同行为,比抽象类的使用更加方便灵活;
② 接口只关心对象之间的交互的方法,而不关心对象所对应的具体类。接口是程序之间的一个协议,比抽象类的使用更安全、清晰。一般使用接口的情况更多。 e.log("数据库通用类")
-
抽象类
abstract class DB { // 构造函数 constructor( public name: string, public port: number, public dbtype: string, ) { } desc() { console.log("数据库通用类") } abstract connect(): void; }
-
接口类
interface IDB { name: string; port: number; connect(): void; } class PostgreSqlDB implements IDB { constructor(public name: string, public port: number) { } connect(): void { console.log("链接到", this.name, this.port) } }
type User = { name: string, age: number, weight: number, } type Role = { role: string, tel: string, } // 合并两个类型 type Staff = User & Role;
// ---------------------- const main = () => {
let user1: User = { name: "Koma", age: 25, weight: 70, } console.log(user1) // 实现一个员工 let staff: Staff = { name: "Koma", age: 25, weight: 70, role: "Admin", tel: "01-1234-5678" } console.log(staff) }
简单的案例
// 这里定义
namespace Utils { // 定义并直接导出让大家使用。 export class DB { constructor( public name: string, public port: number, public dbtype: string, ) { } connect () { console.log(`${this.name}...`) } } } // 这里使用 namespace App { const main = () => { let db: Utils.DB = \ new Utils.DB("mysql", 3306, "RDS") db.connect() } main() }
./src/Utils/DB.ts
namespace Utils { export class DB { // 构造函数 constructor ( public name: string, public port: number, public dbtype: string, ) { } // 数据库连接方法 connect() { console.log(`${this.name}`) } } }
/// <reference path = "Utils/DB.ts" /> // 以上一行是个必须的写法,看着都别扭
namespace App { const main = () => { let db: Utils.DB = new Utils.DB("mysql", 3306, "RDS") db.connect() } main() }
然后,需要再修改下 tsconfig.json
{ "compilerOptions": { // "module": "commonjs", "module": "amd", // Add this new line. "outFile": "./dist/main.js", } }
-
-
参考
-
"模块JS"的一个例子:
import * as util from './util.mjs'
console.log(util) console.log(util.add) console.log(util.SITENAME)
以下才是当前流行的写法,简洁。顺便恢复tsconfig.json原来的样子:commonjs 即可。
直接将 DB 倒进来 成为 ”可用“。
import { DB } from './Utils/DB' // namespace App { const main = () => { let db: DB = new DB("mysql", 3306, "RDS") db.connect() } main() // }