再学Typescript,只为更好的运用它

225 阅读4分钟

其实,之前也学过ts,也在 React16 和 Vue3 的项目中用过,但是,整体功力还不够精湛,没有发挥出ts真正的威力。也因为今年开始,新项目都转战 Vue3,所以后续不管是 Vue 还是 React 的项目,ts 都会是必备搭档。所以用好ts是迫在眉睫了!

之前的学习笔记 TypeScript 入门修炼,在此基础上,更进一步,加深修炼...

思考

其实,Typescript 最核心之一就是 类型!那么类型是什么呢?有很多智者和前辈们已经给我们一语道破:一种思维方式,同时也是错误检查的一柄利器。

一种思维方式:这种类型的思维方式本质就是什么是什么,好比归类,这个是什么类,那个是什么类,在归类的过程中,又衍生什么类...映射到开发中,函数式、面向对象都具有什么方法,什么属性,接着,演化出类似的新方法,类似的新属性等,具备了进化的可能性。那么,类型的思维方式在函数式和面向对象都是高度统一的。(个人简介,欢迎批评指导!)

错误检查的一柄利器:可以在项目开发前期帮助我们提前发现错误,减少程序的错误,让我们的项目更加稳定。

通过思考,我们现有了一定的认知,我们在继续往下飞奔...

TS介绍

2012年微软发布的一门编程语言。发布时就提供了将Typescript翻译到JS的翻译编译器(transcompiler)

  • Typescript是JS的一个超集。

  • TS同时支持Client Side(浏览器)和Server Side(Nodejs)

  • TS是一个多范式语言(类型帮助函数式和面向对象在设计理念上产生统一,所以同时支持函数式和面向对象,是一种多范式语言)

  • TS同时支持Duck Typing(鸭子类型), Gradual Typing(渐进类型)和Strick Typing(严格类型)

环境配置

其实环境配置还是很重要的,一般都是团队中的leader或者架构师给搭建项目,配置项目之类的。所以要想成长为leader 或者架构师,环境配置是必备技能。

ts-node

Node环境的typescript解释执行器。 REPL(read、evaluate、print、loop)

“读取-求值-输出”循环(英语:Read-Eval-Print Loop,简称REPL),也被称做交互式顶层构件(英语:interactive toplevel),是一个简单的,交互式的编程环境。这个词常常用于指代一个Lisp的交互式开发环境,也能指代命令行的模式。---维基百科

npm i -g ts-node
# yarn global add ts-node

用ts-node执行文件

ts-node demo.ts

配置文件:tsconfig.json

{
    "compilerOptions" : {
        /// 书写你的配置
    }
}

tsc(typescript compiler)

一个ts的编译器。

npm i -g typescript
# yarn global add typescript

可以指定编译某个ts文件:

tsc demo.ts

也可以通过tsconfig.json 配置

  • 可以用outDir 配置项配置js文件输出的位置
  • tsc 作为一个指令,可以用--help 查看用法
  • 可以用module指定生成模块的类型

和webpack一起用

初始化一下这个项目:

mkdir ts-webpack
cd ts-webpack
npm init 

先安装依赖:

npm install webpack ts-loader typescript webpack-cli --save-dev
# yarn add webpack ts-loader typescript webpack-cli

写一个用于测试的ts文件:

// src/index.ts
export class TreeNode<T> {
    left: TreeNode<T>
    right: TreeNode<T>
    data: T

    constructor(data: T) {
        this.data = data
    }
}

function log(x) {
    console.log(x)
}

const node = new TreeNode<number>(100)
log(node.data)

写一个tsconfig.json文件:

{
    
}

然后配置一个针对ts文件打包处理的webpack配置。

// webpack.config.js
const path = require('path')

module.exports = {
    entry: {
        index: "./src/index.ts",
    },
    mode: "development",
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader",
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: [".tsx", ".ts", ".jsx", ".js"],
    },
    output: {
        filename: "bundle.[name].js",
        path: path.resolve(__dirname, "dist"),
    }
}

为package.json增加指令:

"scripts": {
    "start:dev": "webpack",
    ...
},

运行起来试一下效果:

npm run start:dev
node ./dist/bundle.index.js
100

React + TS Loader

1、安装npm包

# 先装react依赖
npm i react react-dom --save
npm i @types/react @types/react-dom --save-dev
# yarn add react react-dom
# yarn add -D @types/react @types/react-dom

# 再装个ts-loader和awesome-typescript-loader
npm i ts-loader awesome-typescript-loader --save
# yarn add ts-loader awesome-typescript-loader

# 再装webpack开发用的
npm i webpack-dev-server html-webpack-plugin --save-dev
# yarn add webpack-dev-server html-webpack-plugin -D
  • 注意:不装types会报错
  • webpack-dev-server 用来启动一个网站
  • html-webpack-plugin用于为react程序提供模板

2、编写一个React程序

// src/react.tsx
import React from 'react'
import ReactDOM from 'react-dom'

const App: (() => JSX.Element) = () => {
    return <div>
        <h1>Hello React!</h1>
    </div>
}

ReactDOM.render(<App />, document.getElementById('root'))

因为报错,需要考虑增加 "esModuleInterop": true"jsx": "react"tsconfig.json

esModuleInterop: 让TypeScript分别对待CommonJS/AMD/UMD modules,从而让用户获得统一的体验。

jsx选项需要设置成react,这样typescript会将jsx语法转换为React.createClass。

3、编写一个react.html模板文件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>React</title>
</head>

<body>
    <div id="root"></div>
</body>

</html>

4、编写对应的webpack文件

// webpack.react.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: {
        react: "./src/react.tsx",
    },
    mode: "development",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: "ts-loader",
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: [".tsx", ".ts", ".jsx", ".js"],
    },
    output: {
        filename: "bundle.[name].js",
        path: path.resolve(__dirname, "dist"),
    },
    devServer: {
        static: {
            directory: path.resolve(__dirname, "dist"),
        },
        port: 5900,
        open: true,
    },
    plugins: [new HtmlWebpackPlugin({
        template: path.resolve(__dirname, "react.html")
    })]
}

5、增加package.json脚本

"start:react": "webpack serve --config ./webpack.react.js",

6、尝试执行脚本

npm run start:react

React + Babel

1、补充babel的依赖

npm i babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript --save-dev
# yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript 

思考:babel-preset VS ts-loader 的区别是什么?

  • babel-loader(背后是:babel)
    • babel是干什么的? —— The Compiler for next generation Javascript
      • 所有编译JS的事情,babel都干!
      • es6->es5->es3->polifill
    • 缓存+优化
    • 插件+生态
  • ts-loader(背后是:typescript)
    • ts -> es

2、写一个单独的webpack文件

// webpack.react.addbabel.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    entry: {
        "react-babel": "./src/reactbabel.tsx",
    },
    mode: "development",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            '@babel/preset-typescript',
                            "@babel/preset-react",
                            "@babel/preset-env"
                        ]
                    }
                },
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: [".tsx", ".ts", ".jsx", ".js"],
    },
    output: {
        filename: "bundle.[name].js",
        path: path.resolve(__dirname, "dist"),
    },
    devServer: {
        host: '0.0.0.0',
        static: {
            directory: path.resolve(__dirname, "dist"),
        },
        port: 5901,
        open: true,
    },
    plugins: [new HtmlWebpackPlugin({
        template: path.resolve(__dirname, "react.html")
    })]
}

3、增加npm脚本

"start:react-babel": "webpack serve --config ./webpack.react.addbabel.js",

4、尝试一下

npm run start:react-babel

Vue + Loader

1、增加vue需要的依赖

# Vue 的依赖
npm i vue@next --save
npm i -D  @vue/compiler-sfc 
# yarn add vue@next
# yarn add -D @vue/compiler-sfc

# Vue的loader
npm i vue-loader --save-dev
# yarn add vue-loader -D

sfc - Single File Component

2、写一个Vue的SFC和一个bootstraper

<template>
  <h1>TS + Vue!</h1>
</template>

<script lang='ts'>
export default {
  setup() {
    return {};
  },
};
</script>
// main.ts
import {createApp} from 'vue'
import App from './App.vue'

createApp(App).mount("#root")

写完但是App.vue报错,为啥?因为还需要一个shim文件:

// shims-vue.d.ts
/* eslint-disable */
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

image-20210712030314880.png

shim(垫片),通常为了处理兼容而命名。上面这个shim的目标是让vscode和webpack等知道.vue的文件,可以当做一个组件定义文件来使用。

3、写一个vue的webpack配置

// webpack.vue.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
    entry: "./src/main.ts",
    mode: "development",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                options: {
                    appendTsSuffixTo: [/\.vue$/],
                },
                exclude: /node_modules/,
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
            }
        ],
    },
    resolve: {
        extensions: [".vue", ".tsx", ".ts", ".jsx", ".js"],
    },
    output: {
        filename: "bundle.[name].js",
        path: path.resolve(__dirname, "dist"),
    },
    devServer: {
        host: '0.0.0.0',
        static: {
            directory: path.resolve(__dirname, "dist"),
        },
        port: 5902,
        open: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "vue.html"),
        }),
        new VueLoaderPlugin()
    ],
}

4、增加npm脚本

"start:vue": "webpack serve --config ./webpack.vue.js",

5、试一试

注意tsconfig中的jsx需要设置为preserve,也就是保留由ts-loader处理。

npm run start:vue

Vue + Babel Preset

1、安装依赖

npm i -D babel-preset-typescript-vue3
# yarn add -D babel-preset-typescript-vue3

2、写一个webpack.config文件

// webpack.vue.addbabel.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
    entry: "./src/main.ts",
    mode: "development",
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            "@babel/preset-env",
                            // preset-typescript-vue3只增加了typescript的转化能力
                            "babel-preset-typescript-vue3",
                            "@babel/preset-typescript",
                        ]
                    }
                },
                exclude: /node_modules/,
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
            }
        ],
    },
    resolve: {
        extensions: [".vue", ".tsx", ".ts", ".jsx", ".js"],
    },
    output: {
        filename: "bundle.[name].js",
        path: path.resolve(__dirname, "dist"),
    },
    devServer: {
        host: '0.0.0.0',
        static: {
            directory: path.resolve(__dirname, "dist"),
        },
        port: 5903,
        open: true,
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "vue.html"),
        }),
        new VueLoaderPlugin()
    ],
}

3、配置npm脚本

"start:vue-babel": "webpack serve --config ./webpack.vue.addbabel.js",   

4、执行观察结果

npm run start:vue-babel

具体使用

接下来结合这篇整理的笔记学习,TS只学这一篇就够了TS细节知识点修炼更新