一、TS中的配置文件
基础配置
执行命令
tsc --init
生成tsconfig.json文件,也就是TS的编译配置文件。
{
"compilerOptions": {
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', 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. */
}
}
编译TS命令,直接指定编译某个ts文件的时候并不会使用tsconfig.json中的内容,只有直接运行tsc时,才会走tsconfig.json中的配置
tsc dome.ts
只有直接运行tsc时,才会走tsconfig.json中的配置。在未配置tsconfig.json的情况下,会执行编译所有的ts文件
tsc // 在未配置tsconfig.json的情况下,会执行编译所有的ts文件
tsc命令会找tsconfig.json,
"include": ["./demo.ts"], // 指定只编译demo.ts文件
"exclude": ["./demo.ts"], // 排除编译demo.ts文件,就不会编译demo.ts
"files": ["./demo.ts"], // files与include功能相同
"removeComments": true // 编译中移除注释
也可以写成
"include": ["src/**/*"], // ** 代表任何目录,* 代表任何文件
参考文档: tsconfig-json
compilerOptions字面意思:就是编译过程中的一些属性或配置。
"compilerOptions": {
"removeComments": true, // 编译中移除注释
"noImplicitAny": false, // 不要求显示的设置any,false就是可以,这样变量不定义类型也不会报错。默认true
"strictNullChecks": false, // 强制检查null类型。默认true。否则编译会报错。
}
如
const children: string = null // 不允许把null复制给其他基础类型 strictNullChecks设置为false则不会报错,编译也不会报错。
进阶配置
- 指定文件的输入地址,和文件的生成地址。
"rootDir": "./src", // 输入地址
"outDir": "./build", // 生成地址
- 增量编译。只编译新增内容,之前编译过的不会再编译。打开后,首次编译之后会生成
tsconfig.tsbuildinfo文件,用来记录上次编译过程中的信息,再次编译的时候就会和上一次做比对。是一个增量式的配置项。
"incremental": true, // 增量编译
- 是否允许对js项也进行编译。
"target": "es5", // 将代码编译成es5
"allowJs": true, // 是否允许对js项也进行编译
编译前:
export const name = 'dell'
编译后
"use strict"
Object.defineProperty(exports, "__esModule", {value: true})
exports.name = 'dell'
- 对语法进行检测。
"checkJs": true, // 对js文件进行语法检测
- 生成
sourceMap文件,***.js.map
"sourceMap": true,
- 变量未被使用时警告。
"noUnusedLocals": true,
- 函数的参数未被使用时警告。
"noUnusedParameters": true,
参考文档:编译选项
二、TS中的联合类型Unin type和类型保护
用类型保护解决联合类型遇到的问题
- 类型断言
as
interface Bird {
fly: boolean,
sing: () => {},
}
interface Dog {
fly: boolean,
bark: () => {},
}
// 联合类型 animal: Bird | Dog
function trainAnimal (animal: Bird | Dog) {
// 类型保护 sing和bark是独有的属性
// 类型断言 (animal as Bird) 传入的animal是Bird的时候
if (animal.fly) { // Bird才会飞true
(animal as Bird).sing() // as 断言
} else {
(animal as Dog).bark()
}
}
- 类型保护,用
in语法
function trainAnimalSecond (animal: Bird | Dog) {
if ('sing' in animal) {
animal.sing()
} else {
animal.bark()
}
}
- 类型保护,用
typeof语法
function add (first: string | number, second: string | number) {
if (typeof first === 'string' || typeof second === 'string') {
return `${first}${second}`
}
return first + second
}
- 类型保护,用
instanceof语法,只有为class类的时候才可以使用,interface不能用instanceof
class NumberObj {
public count: number
}
function addSecond (first: object | NumberObj, second: object | NumberObj) {
if (first instanceof NumberObj && second instanceof NumberObj) {
return first.count + second.count
}
return 0
}
二、TS中的枚举类型Enum
- 用js来模拟枚举
const Status = {
OFFLINE: 0,
ONLINE: 1,
DELETED: 2
};
function getResult(status) {
if (status === Status.OFFLINE) {
return 'offline';
} else if (status === Status.ONLINE) {
return 'online';
} else if (status === Status.DELETED) {
return 'deleted';
} else {
return 'error';
}
}
const result = getResult(Status.OFFLINE);
console.log(result); // online
- 用
ts来写枚举
enum Status {
OFFLINE,
ONLINE,
DELETED
}
// 枚举默认值为 0 1 2
console.log(Status.OFFLINE); // 0
console.log(Status.ONLINE); // 1
console.log(Status.DELETED); // 2
function getResult(status) {
if (status === Status.OFFLINE) {
return 'offline';
} else if (status === Status.ONLINE) {
return 'online';
} else if (status === Status.DELETED) {
return 'deleted';
} else {
return 'error';
}
}
const result = getResult(Status.OFFLINE);
const result1 = getResult(0);
console.log(result); // offline
console.log(result1); // offline
输出结果,依旧不变。
改变offline的枚举值
enum Status {
OFFLINE = 1,
ONLINE,
DELETED
}
console.log(Status.OFFLINE); // 1
console.log(Status.ONLINE); // 2
console.log(Status.DELETED); // 3
enum Status {
OFFLINE, // 默认从0 开始
ONLINE = 4,
DELETED // 从4开始加1
}
console.log(Status.OFFLINE); // 0
console.log(Status.ONLINE); // 4
console.log(Status.DELETED); // 5
枚举类型还可以反查结果(反向映射),如下:
enum Status {
OFFLINE,
ONLINE,
DELETED
}
console.log(Status[0]); // OFFLINE
enum是一个更灵活的数据结构。
使用场景:
- 和我们的例子类似,当我们的某一个状态的值是固定的几个值,此时就可以用枚举类型来表示这几个值,更加清晰易懂。
- 枚举默认值从0开始,也可以手动修改枚举值。
- 还可以通过对应下标0、1、2...反向查枚举的值。如果枚举值被修改,则需要写对应修改的值即可,如
console.log(Status[4])为ONLINE
小插曲:在安装typescirpt的时候竟然报了如下错误,原因是项目的name也是typescript,修改之后即可正常安装。
npm ERR! code ENOSELF
npm ERR! Refusing to install package with name "typescript" under a package
npm ERR! also called "typescript". Did you name your project the same
npm ERR! as the dependency you're installing?
npm ERR!
npm ERR! For more information, see:
npm ERR! <https://docs.npmjs.com/cli/install#limitations-of-npms-install-algorithm>
{
"name": "typescript",
"version": "1.0.0",
"description": "",
"main": "index.js",
"devDependencies": {
"ts-node": "^8.6.2"
},
"scripts": {
"dev": "ts-node ./src/enum.ts"
},
"keywords": [],
"author": "",
"license": "ISC"
}
四、TS中的函数泛型
泛型generic:泛指的类型,写法<>
针对不确定的类型就可以用泛型语法,在使用的时候再指定具体的对应值。
示例1
// <ABC> 定义名字为ABC的泛型,两个传入的参数都要求为同一类型
function join<ABC>(first: ABC, second: ABC) {
return `${first}${second}`
}
// 在使用时给ABC指定类型,定义类型都为string,传入的值就必须为string类型
join<string>('1','2')
如下写法,则无法做到两个参数传入值必须为同一类型的要求。
function join(first: string | number, second: string | number) {
return `${first}${second}`
}
join('1',2)
示例2
// function map<ABC> (params: Array<ABC>) {
function map<ABC> (params: ABC[]) {
return params
}
map<string>(['1', '2', '3'])
常见用T来代替ABC,也就是Type的简写
// function map<T> (params: Array<T>) {
function map<T> (params: T[]) {
return params
}
map<string>(['1', '2', '3'])
我们还可以定义两个泛型或多个泛型
function join<T, P>(first: T, second: P) {
return `${first}${second}`;
}
join<string, number>('1', 2); // 显式声明类型
join<('1', 2) // 不写类型则会默认推断
不写类型则会默认推断(类型推断),如下图:
声明返回类型也用T这个泛型,如下:
function anotherJoin<T>(first: T, second: T): T {
return first;
}
anotherJoin<string>('1', '2');
泛型和普通的类型使用基本是一样的。使用的时候需要在函数前用<>做一个声明。
五、TS中类的泛型以及泛型类型
Demo1
class DataManager<T> {
constructor(private data: T[]) {}
getItem(index: number): T {
return this.data[index];
}
}
const data = new DataManager<number>([2]);
data.getItem(0);
Demo2: 让泛型继承某一属性
interface Item {
name: string;
}
class DataManager<T extends Item> {
constructor(private data: T[]) {}
getItem(index: number): string {
return this.data[index].name;
}
}
const data = new DataManager([{ name: '2' }]);
data.getItem(0);
Demo3: 泛型是number或string,<T extends number | string>
class DataManager<T extends number | string> {
constructor(private data: T[]) {}
getItem(index: number): T {
return this.data[index];
}
}
const data = new DataManager<number>([2])
Demo4: 如何使用泛型作为一个具体的类型注解
// 泛型还可以当做type的声明
function hello<T>(params: T) {
return params
}
const func:<T>(param: T) => T = hello
六、TS中命名空间-namespace
过多的全局变量一定会让页面变的不可维护。
编译前:
// 把所有东西都放到Home这样的命名空间中,防止Header、Content、Footer都暴露在全局环境中
namespace Home {
class Header {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Header';
document.body.appendChild(element);
}
}
class Content {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Content';
document.body.appendChild(element);
}
}
class Footer {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Footer';
document.body.appendChild(element);
}
}
export class Page { // 导出Page
constructor() {
new Header();
new Content();
new Footer();
}
}
}
new Home.Page();
编译后:
// 把所有东西都放到Home这样的命名空间中
var Home;
(function (Home) {
var Header = /** @class */ (function () {
function Header() {
var element = document.createElement('div');
element.innerHTML = 'This is Header';
document.body.appendChild(element);
}
return Header;
}());
var Content = /** @class */ (function () {
function Content() {
var element = document.createElement('div');
element.innerHTML = 'This is Content';
document.body.appendChild(element);
}
return Content;
}());
var Footer = /** @class */ (function () {
function Footer() {
var element = document.createElement('div');
element.innerHTML = 'This is Footer';
document.body.appendChild(element);
}
return Footer;
}());
var Page = /** @class */ (function () {
function Page() {
new Header();
new Content();
new Footer();
}
return Page;
}());
Home.Page = Page; // 将Page方法暴露出来
})(Home || (Home = {}));
new Home.Page();
这种写法可以让我们尽少的声明全局变量,把一组相关的内容封装到一起,对外提供统一的接口。
// tsconfig.json
"outFile": "./build/page.js", // 将多个ts文件统一输出到一个js文件中./build/page.js文件中
"module": "amd"
将文件拆成page.ts和components.ts
// components.ts
namespace Components {
// 子命名空间
export namespace SubComponents {
export class Test{}
}
// 导出接口
export interface User {
name: string;
}
export class Header {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Header';
document.body.appendChild(element);
}
}
export class Content {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Content';
document.body.appendChild(element);
}
}
export class Footer {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Footer';
document.body.appendChild(element);
}
}
}
// page.ts
依赖的声明,namespace之间相互引用的声明,用///表示
/// <reference path='./components.ts' />
namespace Home {
export class Page {
user: Components.User = {
name: 'fruit'
}
constructor() {
new Components.Header();
new Components.Content();
new Components.Footer();
}
}
}
new Home.Page();
七、TS中import对应的模块化
amd语法浏览器是不支持的
// components.ts
export class Header {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Header';
document.body.appendChild(element);
}
}
export class Content {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Content';
document.body.appendChild(element);
}
}
export class Footer {
constructor() {
const element = document.createElement('div');
element.innerHTML = 'This is Footer';
document.body.appendChild(element);
}
}
import 模块引入(es6用法)
// page.ts
import { Header, Content, Footer } from './components';
class Page {
constructor() {
new Header();
new Content();
new Footer();
}
}
new Page();
参考文档:
zhuanlan.zhihu.com/p/25107397?…
八、使用Parcel打包TS代码
Parcel:是和webpack类似的打包工具,不需要做过多配置,使用起来相对简单。
npm init -y
tsc --init
parcel : github.com/parcel-bund…
npm install parcel@next -D
// package.json
"scripts": {
"test": "parcel ./src/index.html"
},
运行
npm run test
http://localhost:1234/即可查看相关运行结果
九、描述文件中的全局类型
类型定义文件***.d.ts
如何在.d.ts文件里对全局函数和全局变量进行定义
// jquery.d.ts
// 定义全局变量
declare var $: (param: () => void) => void;
// 定义全局函数
declare function $(param: () => void): void;
// 允许一个函数多次定义。函数的重载,根据传入参数的不同变化
declare function $(params: string): {
html: (html: string) => {}
}
写法优化
// 定义全局函数
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}
// 函数重载
declare function $(readyFunc: () => void): void;
declare function $(selector: string): JqueryInstance;
使用$就不会报错
$(function() {
$('div').html('<div>123</div>');
});
为什么我们要安装或自己写类型定义文件,帮助我们的ts文件,理解js文件或js库中的内容。比如$。
**.d.ts为类型描述文件或类型定义文件
使用interface实现函数重载
// $只是不同的函数,只是对函数重载的时候,可以用下面的语法
interface JQuery {
(readyFunc: () => void): void
(selector: string): JqueryInstance
}
declare var $: JQuery;
// 如何对对象进行类型定义,以及如何对类进行类型定义,以及命名空间的嵌套
// 既让$是函数,又让$是对象,就可以用下面的语法
declare namespace $ { // 在全局有对象,可以用namespace来构建这个对象
namespace fn {
class init {}
}
}
// 引入外部的库,TS无法识别,我们就可以用全局声明内容的语法,让ts能够理解,$里都有哪些内容
$(function() {
$('div').html('<div>123</div>');
new $.fn.init();
});
引入外部的库,TS无法识别,我们就可以用全局声明内容的语法,让TS能够理解,$里都有哪些内容
参考文档:TypeScript error: Property 'X' does not exist on type 'Window'
当我们使用window时,比如 let a = window.a,将a挂载到window下,此时TS会报如下错误:
TypeScript error: Property 'a' does not exist on type 'Window'. TS2339
如下,使用type定义依旧无效
type Window = {
a: any
}
let a = window.a;
解决方法
1. 推荐
declare global {
interface Window {
a:any;
}
}
let a = window.a;
2. 不推荐
declare const window: any;
let a = window.a;
十、模块代码的类型描述文件
es6、commonjs、UMD 等在TS中如何定义
npm install jquery --save
import $ from 'jquery'
报如下错误
ES6 模块化的类型注解文件,就是这么写出来的。Commonjs UMD等写法和ES6写法稍有不同,可查阅相关资料。
// jquery.d.ts
// ES6 模块化
declare module 'jquery' {
interface JqueryInstance {
html: (html: string) => JqueryInstance;
}
// 混合类型
function $(readyFunc: () => void): void;
function $(selector: string): JqueryInstance;
namespace $ {
namespace fn {
function init(): void;
}
}
// 导出
export = $;
}
这样使用$就不会报错了
import $ from 'jquery';
$(function() {
$('div').html('<div>123</div>');
new $.fn.init();
});
十一、泛型中keyof语法的使用
interface Persone {
name: string;
age: number;
gender: string;
}
class Student {
constructor(private info: Persone) {}
getInfo(key: string) {
return this.info[key]; // 我们无法确定key的值
}
}
const student1 = new Student({
name: 'fruit',
age: 18,
gender: 'male'
});
const studentName = student1.getInfo('name');
studentName的类型返回的为any
key值是不安全的,当传入的值为name、age、gender之外的值时,就会返回undefined,因此我们需要类型保护来解决这个问题。
修改写法:但不够好,传入这三个值之外的值是,还是会返回undefined
getInfo(key: string) {
if (key === 'name' || key === 'age' || key === 'gender') {
return this.info[key];
}
}
泛型结合keyof来写
type T = 'name'
key: 'name'
Person1['name']
type T = 'age'
key: 'age'
Person1['age']
type T = 'gender'
key: 'gender'
Person1['gender']
interface Person1 {
name: string;
age: number;
gender: string;
}
class Student {
constructor(private info: Person1) {}
getInfo<T extends keyof Person1>(key: T): Person1[T] {
return this.info[key];
}
}
const student1 = new Student({
name: 'fruit',
age: 18,
gender: 'male'
});
const studentName = student1.getInfo('hello'); // 不能传hello,只能是name、age、gender
type T = 'name',类型的值就是一个字符串。
type A = 'NAME'
const a: A = 'bc'
报如下错误:
// 这样就不报错,变量的值必须和type中'NAME'字符串一样的东西
type A = 'NAME'
const a: A = 'NAME'
因此,类型也可以是一个字符串。正是因为这个原理,我们才可以用keyof语法,结合泛型,实现上述效果(只能传递对应的key)。如果有一个类,里面有一个对象,我们想根据index、或key值获取对象里的某项内容时,又想要推断出正确的返回类型,就可以用类似这样的语法来解决<T extends keyof Person1>(key: T): Person1[T]