![]() |
一直单身的饭卡 · rwkv模型lora微调之accelerat ...· 5 月前 · |
![]() |
谈吐大方的鸵鸟 · 华为adb彻底关闭系统更新-掘金· 1 年前 · |
![]() |
苦恼的山羊 · QGraphicsItem的hoverMov ...· 1 年前 · |
![]() |
活泼的硬盘 · 如何在JavaScript中使用ISO860 ...· 1 年前 · |
![]() |
独立的饺子 · python - Why is ...· 2 年前 · |
module
关键字既可以称做“内部模块”,也可以称做“外部模块”。这让刚刚接触 TypeScript 的开发者会有些困惑。
之前
module Math {
export function add(x, y) { ... }
之后
namespace Math {
export function add(x, y) { ... }
// tsconfig.json
/* 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. */
// 对 null 类型检查,设置为 false 就不会报错了
// "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. */
let aaa: [string, number] = ['aaa', 5];
// 添加时不会报错
aaa.push(6);
// 打印整个元祖不会报错
console.log(aaa); // ['aaa',5,6];
// 打印添加的元素时会报错
console.log(aaa[2]); // error
enum Gender {
BOY = 1,
console.log(Gender.BOY);// 1
console.log(Gender);// { '1': 'BOY', '2': 'GRIL', BOY: 1, GRIL: 2 }
enum Char {
// const member 常量成员:在编译阶段被计算出结果
a, // 没有初始值
b = Char.a,// 对常量成员的引用
c = 1 + 3, // 常量表达式
// computed member 计算成员:表达式保留到程序的执行阶段
d = Math.random(),// 非常量表达式
e = '123'.length,
// 紧跟在计算成员后面的枚举成员必须有初始值
f = 6,
const enum Colors {
Yellow,
// 常量枚举会在编译阶段被删除
let myColors = [Colors.Red, Colors.Yellow, Colors.Blue];
编译成 JS
"use strict";
var myColors = [0 /* Red */, 1 /* Yellow */, 2 /* Blue */];
// 报错
const enum Color {Red, Yellow, Blue = "blue".length};
console.log(Colors.RED);
以下代码存在的问题:
function initByRole(role) {
if (role === 1 || role == 2) {
console.log("1,2")
} else if (role == 3 || role == 4) {
console.log('3,4')
} else if (role === 5) {
console.log('5')
} else {
console.log('')
使用枚举后
enum Role {
Reporter,
Developer,
Maintainer,
Owner,
Guest
function init(role: number) {
switch (role) {
case Role.Reporter:
console.log("Reporter:1");
break;
case Role.Developer:
console.log("Developer:2");
break;
case Role.Maintainer:
console.log("Maintainer:3");
break;
case Role.Owner:
console.log("Owner:4");
break;
default:
console.log("Guest:5");
break;
init(Role.Developer);
// 数字索引——约束数组
// index 是随便取的名字,可以任意取名
// 只要 index 的类型是 number,那么值的类型必须是 string
interface StringArray {
// key 的类型为 number ,一般都代表是数组
// 限制 value 的类型为 string
[index:number]:string
let arr:StringArray = ['aaa','bbb'];
console.log(arr);
// 字符串索引——约束对象
// 只要 index 的类型是 string,那么值的类型必须是 string
interface StringObject {
// key 的类型为 string ,一般都代表是对象
// 限制 value 的类型为 string
[index:string]:string
let obj:StringObject = {name:'ccc'};
// 注意区别
// 普通的接口
interface discount1{
getNum : (price:number) => number
// 函数类型接口
interface discount2{
// 注意:
// “:” 前面的是函数的签名,用来约束函数的参数
// ":" 后面的用来约束函数的返回值
(price:number):number
let cost:discount2 = function(price:number):number{
return price * .8;
// 也可以使用类型别名
type Add = (x: number, y: number) => number
let add: Add = (a: number, b: number) => a + b
// 接口可以在面向对象编程中表示为行为的抽象
interface Speakable {
name: string;
// ":" 前面的是函数签名,用来约束函数的参数
// ":" 后面的用来约束函数的返回值
speak(words: string): void
interface Speakable2 {
age: number;
class Dog implements Speakable, Speakable2 {
name!: string;
age = 18;
speak(words: string) {
console.log(words);
let dog = new Dog();
dog.speak('汪汪汪');
interface FnType {
(getName:string):string;
interface MixedType extends FnType{
name:string;
age:number;
interface Counter {
(start: number): string;
interval: number;
reset(): void;
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
function attr(val: string): string;
function attr(val: number): number;
// 前面两行是函数申明,这一行是实现函数重载
function attr(val: any): any {
if (typeof val === 'string') {
return val;
} else if (typeof val === 'number') {
return val;
attr('aaa');
attr(666);
// 后写的接口中的函数声明优先级高
interface Cloner111 {
clone(animal: Animal): Animal;
interface Cloner111 {
clone(animal: Sheep): Sheep;
interface Cloner111 {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
// ==> 同名接口会合并
// 后写的接口中的函数声明优先级高
interface Cloner111 {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
interface Cloner222 {
// 接口内部按书写的顺序来排,先写的优先级高
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
class Father {
str: string; // 默认就是 public
public name: string; // 在定义的类中、类的实例、子类、子类实例都可以访问
protected age: number; // 只能在定义的类和子类中访问,不允许通过实例(定义的类的实例和子类实例)访问
private money: number; // 只能在定义的类中访问,类的实例、子类、子类实例都不可以访问
constructor(name: string, age: number, money: number) {
this.name = name;
this.age = age;
this.money = money;
getName(): string {
return this.name;
setName(name: string): void {
this.name = name;
const fa = new Father('aaa', 18, 1000);
console.log(fa.name);// aaa
console.log(fa.age);// error
console.log(fa.money);// error
class Child extends Father {
constructor(name: string, age: number, money: number) {
super(name, age, money);
desc() {
console.log(`${this.name} ${this.age} ${this.money}`);
let child = new Child('bbb', 18, 1000);
console.log(child.name);// bbb
console.log(child.age);// error
console.log(child.money);// error
class Animal {
speak(word: string): string {
return '动作叫:' + word;
class Cat extends Animal {
speak(word: string): string {
return '猫叫:' + word;
let cat = new Cat();
console.log(cat.speak('hello'));
/**--------------------------------------------**/
function double(val: number): number
function double(val: string): string
function double(val: any): any {
if (typeof val == 'number') {
return val * 2;
return val + val;
let r = double(1);
console.log(r);
class Animal {
speak(word: string): string {
return 'Animal: ' + word;
class Cat extends Animal {
speak(word: string): string {
return 'Cat:' + word;
class Dog extends Animal {
speak(word: string): string {
return 'Dog:' + word;
let cat = new Cat();
console.log(cat.speak('hello'));
let dog = new Dog();
console.log(dog.speak('hello'));
// 我们希望传入的值是什么类型,返回的值就是什么类型
// 传入的值可以是任意的类型,这时候就可以用到 泛型
// 如果使用 any 的话,就失去了类型检查的意义
function createArray1(length: any, value: any): Array<any> {
let result: any = [];
for (let i = 0; i < length; i++) {
result[i] = value;
return result;
let result = createArray1(3, 'x');
console.log(result);
// 最傻的写法:每种类型都得定义一种函数
function createArray2(length: number, value: string): Array<string> {
let result: Array<string> = [];
for (let i = 0; i < length; i++) {
result[i] = value;
return result;
function createArray3(length: number, value: number): Array<number> {
let result: Array<number> = [];
for (let i = 0; i < length; i++) {
result[i] = value;
return result;
// 或者使用函数重载,写法有点麻烦
function createArray4(length: number, value: number): Array<number>
function createArray4(length: number, value: string): Array<string>
function createArray4(length: number, value: any): Array<any> {
let result: Array<number> = [];
for (let i = 0; i < length; i++) {
result[i] = value;
return result;
createArray4(6, '666');
使用泛型
// 有关联的地方都改成 <T>
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
return result;
// 使用的时候再指定类型
let result = createArray<string>(3, 'x');
// 也可以不指定类型,TS 会自动类型推导
let result2 = createArray(3, 'x');
console.log(result);
parameterName is Type
这种形式,其中
parameterName
必须是当前函数签名里的一个参数名
interface Bird {
fly()
layEggs()
interface Fish {
swim()
layEggs()
function getSmallPet():Fish | Bird{
return ;
let pet = getSmallPet();
pet.layEggs();
// 当使用联合类型时,如果不用类型断言,默认只会从中获取共有的部分
(pet as Fish).swim();
pet.swim();
image.png
interface Bird {
fly()
layEggs()
interface Fish {
swim()
layEggs()
function getSmallPet():Fish | Bird{
return ;
let pet = getSmallPet();
// 使用类型谓词
function isFish(pet:Fish | Bird):pet is Fish {
return (pet as Fish).swim !== undefined;
if(isFish(pet)){
pet.swim();
}else{
pet.fly();
image.png
?.
?.
计算为 undefined 或 null,则表达式求值为 undefined 。否则,正常触发目标属性访问、方法或函数调用。
a?.b;
// 相当于 a == null ? undefined : a.b;
// 如果 a 是 null/undefined,那么返回 undefined,否则返回 a.b 的值.
a?.[x];
// 相当于 a == null ? undefined : a[x];
// 如果 a 是 null/undefined,那么返回 undefined,否则返回 a[x] 的值
a?.b();
// 相当于a == null ? undefined : a.b();
// 如果 a 是 null/undefined,那么返回 undefined
// 如果 a.b 不是函数的话,会抛类型错误异常,否则计算 a.b() 的结果
let root: any = document.getElementById('root');
root.style.color = 'red';
let root2: (HTMLElement | null) = document.getElementById('root');
// 非空断言操作符--> 这样写只是为了骗过编译器,防止编译的时候报错,打包后的代码可能还是会报错
root2!.style.color = 'red';
false
。
const data = {
str:'',
// num:0,
flag:false,
// flag: null,
// data.str 为 "" 时
let str1 = data.str || '空' // '空'
// data.num 为 0 时
let num1 = data.num || 666 // 666
// data.flag 为 false 时
let status1 = data.flag || true // true
// data.str 为 "" 时,可以通过。仅在 str 为 undefined 或者 null 时,不可以通过
let st2r = data.str ?? '空';
// data.num 为 0 时,可以通过。仅在 num 为 undefined 或者 null 时,不可以通过
let num2 = data.num ?? 666;
// data.flag 为 false 时,可以通过。仅在 flag 为 undefined 或者 null 时,不可以通过
let status2 = data.flag ?? true;
console.log('str=>', str2);
console.log('num=>', num2);
console.log('status=>', status2);
class Greeter {
static message = 'hello';
greet(){
return Greeter.message;
// 获取的是实例的类型,该类型可以获取实例对象上的属性/方法
let greeter1:Greeter = new Greeter();
console.log(greeter1.greet());// 'hello'
// 获取的是类的类型,该类型可以获取类上面的静态属性/方法
let greeterTwo:typeof Greeter = Greeter;
greeterTwo.message = 'hey';
let greeter2:Greeter = new greeterTwo();
console.log(greeter2.greet());// 'hey'
interface Bird {
fly()
layEggs()
interface Fish {
swim()
layEggs()
function getSmallPet():Fish | Bird{
return ;
let pet = getSmallPet();
pet.layEggs();
// 当使用联合类型时,在类型未确定的情况下,默认只会从中获取共有的部分
// 需要使用类型断言
(pet as Fish).swim();
pet.swim();
image.png
enum KindType{
square = 'square',
rectangle = 'rectangle',
circle = 'circle',
interface Square {
kind: KindType.square;
size: number;
interface Rectangle {
kind: KindType.rectangle;
width: number;
height: number;
interface Circle {
kind: KindType.circle;
radius: number;
type Shape = Square | Rectangle | Circle;
function area1(s: Shape) {
// 如果联合类型中的多个类型,拥有共有的属性,那么就可以凭借这个属性来创建不同的类型保护区块
// 这里 kind 是共有的属性
switch (s.kind) {
case KindType.square:
return s.size * s.size;
case KindType.rectangle:
return s.height * s.width;
default:
return;
// 以上代码有隐患,如果后续新增类型时,TS 检查以上代码时,虽然缺失后续新增的类型,但不会报错
console.log(area1({kind: KindType.circle, radius: 1}));
function area2(s: Shape) {
switch (s.kind) {
case KindType.square:
return s.size * s.size;
case KindType.rectangle:
return s.height * s.width;
case KindType.circle:
return Math.PI * s.radius ** 2;
default:
// 检查 s 是否是 never 类型
// 如果是 never 类型,那么上面的分支语句都被覆盖了,就永远都不会走到当前分支
// 如果不是 never 类型。就说明前面的分支语句有遗漏,需要补上
return ((e: never) => {
throw new Error(e)
})(s)
console.log(area2({kind: KindType.circle, radius: 1}));
let name: string;
// 加了 export 后就不会报错
// export {}
image.png
你可能会写出下面这样的代码: 将命名空间导出
shapes.ts
export namespace Shapes {
export class Triangle { /* ... */ }
export class Square { /* ... */ }
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Shapes.Triangle();
不应该在模块中使用命名空间或者说将命名空间导出: 使用命名空间是为了提供逻辑分组和避免命名冲突,模块文件本身已经是一个逻辑分组,并且它的名字是由导入这个模块的代码指定,所以没有必要为导出的对象增加额外的模块层。
下面是改进的例子:
shapes.ts
export class Triangle { /* ... */ }
export class Square { /* ... */ }
shapeConsumer.ts
import * as shapes from "./shapes";
let t = new shapes.Triangle();
或者
shapes.ts
namespace Shapes {
export class Triangle { /* ... */ }
export class Square { /* ... */ }
shapeConsumer.ts
let t = new Shapes.Triangle();
interface String {
// 这里是扩展,不是覆盖,所以放心使用
double(): string;
String.prototype.double = function () {
return this + '+' + this;
console.log('hello'.double());
// 如果加了这个,就会报错
// export {}
interface Window {
myname: string
// 注意:这里的 window 要小写
console.log(window);
// 如果加了这个,当前模块就会变成局部的
// 然后定义的类型 Window 就是局部的变量,不再是一个全局变量
// 所以上面给 Window 扩展属性/方法就失效了
export {}
// exports === module.exports // 即:这两个变量共用一个内存地址
// 整体导出
// module.exports = {}
// 导出多个变量
exports.c = 3;
exports.d = 4;
// 兼容性写法只在 TS 中有效 !!!!!!
// 兼容性写法只在 TS 中有效 !!!!!!
// 兼容性写法只在 TS 中有效 !!!!!!
// a.es6.ts
// 这里只能导出一个
export = function () {
console.log("I'm default")
// b.node.ts
import fn = require('./a.es6.ts');
fn();
.tsx
文件里禁用了使用尖括号的类型断言。
as
操作符在
.ts
文件和
.tsx
文件里都可用
interface Person {
name: string;
age: number
let p1 = {age: 18} as Person;
console.log(p1.name);
// 这种写法在 .tsx 文件中会报错
let p2 = <Person>{age: 18};
console.log(p2.name);
checkJs:true
,对
.js
文件进行类型检查和错误提示。
.js
文件顶部添加
// @ts-nocheck
注释,让编译器忽略当前文件的类型检查。
checkJs:true
并在
.js
文件顶部添加一个
// @ts-check
注释,让编译器检查当前文件。
// @ts-ignore
来忽略本行的错误。
.js
文件里,类型可以和在
.ts
文件里一样被推断出来。当类型不能被推断时,可以通过 JSDoc 来指定类型。
/** @type {number} */
var x;
x = 0; // OK
x = false; // Error: boolean is not assignable to number
/* 错误 */
function reverse(s: String): String;
/* OK */
function reverse(s: string): string;
// error
function f({ x: number }) {
console.log(x);
// ok
function f({x}: { x: number } = {x: 0}) {
console.log(x);
interface Test {
arr: string[]
// pick 摘取返回的结果 => {arr: string[]}
let aaa: Pick<Test, 'arr'> = {arr: ['1']};
const map = new Map([
['F', 'no'],
['T', 'yes'],
for (let key of map.keys()) {
console.log(key);
// 用 forEach 也可以遍历
map.forEach((value,key) => {
console.log(key);
TS2569: Type 'Map' is not an array type or a string type. Use compiler. option '- downlevellteration' to allow iterating of iterators.
配置 dom.iterable 和 downlevelIteration 就可以正常运行 tsconfig.json
{
/*当目标是ES5或ES3的时候提供对for-of、扩展运算符和解构赋值中对于迭代器的完整支持*/
"downlevelIteration": true,
"lib": [
"dom",
"es5",
"es6",
"es7",
"dom.iterable"
注意:如果未指定--lib,则会注入默认的库列表。注入的默认库是: ► For --target ES5: DOM,ES5,ScriptHost ► For --target ES6: DOM,ES6,DOM.Iterable,ScriptHost
interface User {
username: string
id: number
token: string
avatar: string
role: string
type UserWithoutToken = Omit<User, 'token'>
有时候是被 tsconfig.json 自动加入的,如果编译器识别出一个文件是模块导入目标,它就会加到编译列表里,不管它是否被排除了。因此,要从编译列表中排除一个文件,你需要在排除它的同时,还要排除所有对它进行 import 或使用了 ///指令的文件。
// a.ts
namespace Shape {
const pi = Math.PI;
export function cricle(r: number) {
return pi * r ** 2
// b.ts
// 直接使用
// console.log(Shape.cricle(2));
// 或者通过以下方式来使用该命名空间中的变量/函数/类
// import newName = a.b.c.d 用来给常用的、层级较深的对象起一个短的名字
// 这里的 import 的作用是创建一个别名,为任意标识符创建别名,包括导入的模块中的对象
// 不要与用来加载模块的 import x from "module-name" 语法弄混了
import cricle = Shape.cricle;
console.log(cricle(2));
require
关键字,而是直接使用导入符号的限定名赋值。 这与使用
var
相似,但它还适用于类型和导入的具有命名空间含义的符号。 重要的是,对于值来讲,
import
会生成与原始符号不同的引用,所以改变别名的
var
值并不会影响原始变量的值。
{
"compilerOptions": {
/**************基础配置**************/
/**************基础配置**************/
/**************基础配置**************/
/* 开启增量编译:TS 编译器在第一次编译的时候,会生成一个存储编译信息的文件,下一次编译的时候,会根据这个文件进行增量的编译,以此提高 TS 的编译速度 */
// "incremental": true,
/* 指定存储增量编译信息的文件位置 */
// "tsBuildInfoFile": "./",
/* 打印诊断信息 */
// "diagnostics": true,
/* 打印输出的文件 */
// "listEmittedFiles": true,
/* 打印编译的文件(包括引用的声明文件)*/
// "listFiles": true,
/* 指定 ECMAScript 的目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
// "target": "es5",
/* 指定模块代码的生成方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "module": "commonjs",
/* 指定要包含在编译中的库文件——引用类库——即申明文件,如果输出的模块方式是 es5,就会默认引入 "dom","es5","scripthost" */
/* 如果在 TS 中想要使用一些 ES6 以上版本的语法,就需要引入相关的类库 */
// "lib": [],
/* 允许编译 JS 文件 */
// "allowJs": true,
/* 检查 JS 文件*/
// "checkJs": true,
/* 指定 JSX 代码生成的模式: 'preserve', 'react-native', or 'react'. */
/* 'react' 模式下:TS 会直接把 jsx 编译成 js */
/* 'preserve' 模式下:TS 不会把 jsx 编译成 js,会保留 jsx */
// "jsx": "preserve",
/**************声明文件相关配置**************/
/**************声明文件相关配置**************/
/**************声明文件相关配置**************/
/* 生成相应的类型声明文件 —— '.d.ts' */
// "declaration": true,
/* 声明文件的输出路径 */
// "declarationDir": "./d",
/* 只生成声明文件,不生成 JS */
// "emitDeclarationOnly": true,
/* 声明文件目录,默认 node_modules/@types */
// "typeRoots": [],
/* 要导入的声明文件包,默认导入上面声明文件目录下的所有声明文件 */
// "types": [],
/* 将多个相互依赖的文件合并并且把编译后的内容输出到一个文件里
* 可以用在产出 AMD 模块的场景中
* "module":"amd" 时,当一个模块引入了另外一个模块,编译的时候会把这两个模块的编译结果合并到一个文件中
// "outFile": "./",
/* 指定编译文件的输出目录 */
// "outDir": "./out",
/* 指定输入文件的根目录,用于控制输出目录的结构 */
// "rootDir": "./",
/* 启用项目编译 */
// "composite": true,
/* 输出的时候移除注释 */
// "removeComments": true,
/* 不输出文件 */
// "noEmit": true,
/* 发生错误时不输出文件 */
// "noEmitOnError": true,
/* 不生成 helper 函数,以前的话设置为 true 后,需要额外安装 ts-helpers */
/* 类似于 babel ,会给每个文件都生成 helper 函数,会使得最终编译后的包的体积变大 */
// "noEmitHelpers": true,
/* 现在可以通过 tslib(TS 内置的库)引入 helper 函数,!!!文件必须是模块 !!! */
/* 编译后自动引入 var tslib_1 = require("tslib") */
// "importHelpers": true,
/* 当目标是 ES5 或 ES3 的时候提供对 for-of、扩展运算符和解构赋值中对于迭代器的完整支持 */
// "downlevelIteration": true,
/* 把每一个文件转译成一个单独的模块 */
// "isolatedModules": true,
/**************严格检查配置**************/
/**************严格检查配置**************/
/**************严格检查配置**************/
/* 开启所有的严格检查配置 */
"strict": true,
/* 不允许使用隐式的 any 类型 */
// "noImplicitAny": true,
/* 不允许把 null、undefined 赋值给其他类型变量 */
// "strictNullChecks": true,
/* 不允许函数参数双向协变 */
// "strictFunctionTypes": true,
/* 使用 bind/call/apply 时,严格检查函数参数类型 */
// "strictBindCallApply": true,
/* 类的实例属性必须初始化 */
// "strictPropertyInitialization": true,
/* 不允许 this 有隐式的 any 类型,即 this 必须有明确的指向*/
// "noImplicitThis": true,
/* 在严格模式下解析并且向每个源文件中注入 "use strict" */
// "alwaysStrict": true,
/**************额外的语法检查配置,这种检查交给 eslint 就行,没必要配置**************/
/**************额外的语法检查配置,这种检查交给 eslint 就行,没必要配置**************/
/**************额外的语法检查配置,这种检查交给 eslint 就行,没必要配置**************/
/* 有未使用到的本地变量时报错 */
// "noUnusedLocals": true,
/* 有未使用到的函数参数时报错 */
// "noUnusedParameters": true,
/* 每个分支都要有返回值 */
// "noImplicitReturns": true,
/* 严格校验 switch-case 语法 */
// "noFallthroughCasesInSwitch": true,
/**************模块解析配置**************/
/**************模块解析配置**************/
/**************模块解析配置**************/
/* 指定模块的解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)*/
/* 若未指定,那么在使用了 --module AMD | System | ES2015 时的默认值为 Classic,其它情况时则为 Node */
// "moduleResolution": "node",
/* 在解析非绝对路径模块名的时候的基准路径 */
// "baseUrl": "./",
/* 基于 'baseUrl' 的路径映射集合 */
// "paths": {},
/* 将多个目录放在一个虚拟目录下,用于运行时 */
/* 当自己编写的库和开发的代码都输出到一个目录下时,开发代码和库的位置不一样,开发代码引入库的路径就会不对 */
// "rootDirs": [],
// "rootDirs": ["src","out"],
/* 允许 export = xxx 导出 ,并使用 import xxx form "module-name" 导入*/
// "esModuleInterop": true,
/* 当模块没有默认导出的时候,允许被别的模块默认导入,这个在代码执行的时候没有作用,只是在类型检查的时候生效 */
// "allowSyntheticDefaultImports": true,
/* 不要 symlinks 解析的真正路径 */
// "preserveSymlinks": true,
/* 允许在模块中以全局变量的方式访问 UMD 模块内容 */
// "allowUmdGlobalAccess": true,
/************** Source Map 配置**************/
/************** Source Map 配置**************/
/************** Source Map 配置**************/
/* 指定 ts 文件位置 */
// "sourceRoot": "",
/* 指定 map 文件存放的位置 */
// "mapRoot": "",
/* 生成目标文件的 sourceMap */
// "sourceMap": true,
/* 将代码与sourcemaps生成到一个文件中,要求同时设置了--inlineSourceMap 或--sourceMap 属性*/
// "inlineSources": true,
/* 生成目标文件的 inline sourceMap —— 源文件和 sourcemap 文件在同一文件中,而不是把 map 文件放在一个单独的文件里*/
// "inlineSourceMap": true,
/* 生成声明文件的 sourceMap */
// "declarationMap": true,
/************** 实验性的配置**************/
/************** 实验性的配置**************/
/************** 实验性的配置**************/
/* 启用装饰器 */
// "experimentalDecorators": true,
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/**************高级配置**************/
/**************高级配置**************/
/**************高级配置**************/
/* 强制区分大小写 */
// "forceConsistentCasingInFileNames": true
/* 指定需要编译的单个文件列表 */
// "files": [],
/* 指定需要编译的文件/目录 */
// "include": [
// // 只写一个目录名等价于 "./src/**/*"
// "src"
// ]
/* 需要排除的文件或目录 */
// "exclude": []
/* 配置文件继承 */
// "extends": "./tsconfig.base.json"
模式 |
输入 |
输出 |
输出文件扩展名 |
---|---|---|---|
preserve |
<div /> |
<div /> |
.jsx |
react |
<div /> |
React.createElement("div") |
.js |
react-native |
<div /> |
<div /> |
.js |
TypeScript
时,会顺带安装
lib.d.ts
等声明文件,此文件
包含了 JavaScript 运行时以及 DOM 中存在各种常见的环境声明
。
tsconfig.json
中的 lib 选项用来指定当前项目需要注入哪些声明库文件。如果没有指定,默认注入的库文件列表为:
--target ES5
:
DOM,ES5,ScriptHost
--target ES6
:
DOM,ES6,DOM.Iterable,ScriptHost
ES7
、
DOM.Iterable
https://www.tslang.cn/docs/handbook/module-resolution.html
// 配置前
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// 配置后
import React from 'react';
import ReactDOM from 'react-dom';
{
"paths": {
// 这里的路径后面必须跟着 "/*"
"@public/*": [
// 这里的路径后面必须跟着 "/*"
"public/*"
"@src/*": [
"src/*"
"@assets/*":[
"src/assets/*"
"@components/*": [
"src/components/*"
image.png
import styles from './login.less';
import logo from '@assets/images/logo.svg';
const logo2 = require('@assets/images/logo.svg');
console.log(logo2);// path
image.png
解决办法: 给这些非 JS 模块添加申明
/**
* style
declare module '*.css'
declare module '*.less'
// declare module "*.less" {
// const styles: { [className: string]: string };
// export default styles
declare module '*.scss'
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
{
// 允许 默认导入 没有设置默认导出(export default xxx)的模块
// 可以以 import xxx from 'xxx' 的形式来引入模块
"allowSyntheticDefaultImports":true
// 配置前
import * as React from 'react';
import * as ReactDOM from 'react-dom';
// 配置后
import React from 'react';
import ReactDOM from 'react-dom';
.babelrc
{
"presets": [
"@babel/preset-react",
"@babel/preset-env"
"plugins": [
"import",
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css"
/* `style: true` 会加载 less 文件*/
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"jsx": "preserve",// 保留 jsx
webpack.config.js
{
test: /\.tsx?$/,
use: [
'babel-loader',
'ts-loader'
// 源码
// interface RefObject<T> {
// readonly current: T | null;
const ref1:React.RefObject<HTMLDivElement> = React.createRef();
const inputRef = React.createRef<Comp>();
class EditScene extends React.Component<Props> {
inputRef:React.RefObject<Comp>
constructor(props) {
super(props);
this.inputRef = React.createRef<Comp>();
https://segmentfault.com/a/1190000016047027
import {ComponentClass} from 'react'
import {
connect as nativeConnect,
MapDispatchToPropsParam,
MapStateToPropsParam
} from 'react-redux'
import {withRouter as nativeWithRouter} from 'react-router-dom'
export type ComponentDecorator<P = any> = <T extends ComponentClass<P>>(WrappedComponent: T) => T
export const connect: <P, S>(
mapState: MapStateToPropsParam<Partial<P>, P, S>,
// mapDispatch?: MapDispatchToPropsParam<Partial<P>, P>
mapDispatch?: any
) => ComponentDecorator = nativeConnect as any;
export const withRouter: ComponentDecorator = nativeWithRouter as any;
// rootReducer.ts
import {combineReducers} from 'redux';
import {connectRouter} from 'connected-react-router';
import history from '../history';
import evidenceEdit from './evidence';
import common from './common';
import work from './work';
import setScene from './set-scene';
let reducers = {
common,
work,
setScene,
evidenceEdit,
router: connectRouter(history)
// 使用 ReturnType 从 rootReducer 推断状态形状
// export type AppState = ReturnType<typeof rootReducer>
export type AppState = {
[key in keyof typeof reducers]: ReturnType<typeof reducers[key]>
const rootReducer = combineReducers(reducers);
export default rootReducer;
// setScene 模块
import * as types from '../types/action-types';
import {appEditAction} from '../actions/common';
export interface SetSceneState {
loadSuccess: boolean;
loadProgress: number;
let initState: SetSceneState = {
loadSuccess: false,
loadProgress: 0,
export default function (state: SetSceneState = initState, action: appEditAction) {
switch (action.type) {
case types.SCENE_DATA_LOADSUCCESS: {
return {...state, loadSuccess: action.payload.success};
case types.SCENE_DATA_LOADINGPROGRESS: {
return {...state, loadProgress: action.payload.num};
default:
return state;
使用
image.png
// 在 Mesh 组件中
import workActions from "@store/actions/work";
interface MeshProps {
// 刚开始我是这样写的,每次都得在组件的 Props 里重新声明一下函数
// updateSceneData?: (workId: string,data) => appEditAction;
updateData?: typeof workActions.updateData;
@connect(null, {
updateData: workActions.updateData,
class Mesh extends React.Component<MeshProps> {...}
// store/actions/work.ts
import * as types from '../types/action-types';
import {appEditAction} from "@edit-store/actions/common";
export default {
updateWorkData(workId: string, data: any): appEditAction {
return {type: types.UPDATE_WORK_ASYNC, payload: {workId, data}}
import * as React from 'react';
import {RouteComponentProps} from 'react-router';
import {connect} from "@store/connect";
import {AppState} from "@store/reducers";
import commonActions from "@store/actions/commonActions";
// 组件可能有四个属性来源
// 1.mapStateToProps 的返回值
// 2.actions 对象类型
// 3.来自路由
// 4.父组件传进来的其它属性
// 原先的写法:一个个拼起来,mapStateToProps 返回的状态还得在 Props 接口里再声明一遍,比较混乱、麻烦
// interface Props {
// loadProgress?: number;
// markVisible?: boolean;
// setMarkVisible?: typeof commonActions.setMarkVisible;
function mapStateToProps(state: AppState) {
const {markVisible,loadProgress} = state;
return {
markVisible,
loadProgress,
// 现在的写法:便捷
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof commonActions;
interface IParams {}
type RouteProps = RouteComponentProps<IParams>;
type Props = StateProps & RouteProps & DispatchProps & {};
@connect(mapStateToProps, {
setMarkVisible: commonActions.setMarkVisible
export default class App extends React.PureComponent<Props, any> {
render() {
const {markVisible, loadProgress} = this.props;
return (<div > {markVisible} {loadProgress} </div>);
redux thunk 有一个内置类型
ThunkAction
,我们可以这样使用:
// src/thunks.ts
import { Action } from 'redux'
import { sendMessage } from './store/chat/actions'
import { AppState } from './store'
import { ThunkAction } from 'redux-thunk'
export const thunkSendMessage = (
message: string
): ThunkAction<void, AppState, null, Action<string>> => async dispatch => {
const asyncResp = await exampleAPI()
dispatch(
sendMessage({
message,
user: asyncResp,
timestamp: new Date().getTime()
function exampleAPI() {
return Promise.resolve('Async')
# 下载这个类型声明文件
$ npm install --save @types/webpack-env
if (process.env.NODE_ENV !== 'production') {
if (module.hot) {
module.hot.accept('./reducers', () => store.replaceReducer(rootReducer));
image.png
interface Greeting {
name: string;
age: number;
const Hello:React.FC<Greeting> = (props) => <h1>Hello {props.name}</h1>;
// 推荐使用第二种
const Hello2 = (props:Greeting) => <h1>Hello {props.name}</h1>;
import React, { Component } from 'react';
import HelloClass from './HelloClass';
interface Loading {
loading: boolean
// HOC 可以接收一个类组件,也可以接收一个函数组件,所以参数的类型是 React.ComponentType
// 源码:type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
function HelloHOC<P>(WrappedComponent: React.ComponentType<P>) {
return class extends Component<P & Loading> {
render() {
const { loading, ...props } = this.props;
return loading ? <div>Loading...</div> : <WrappedComponent { ...props as P } />;
export default HelloHOC(HelloClass);
class Login extends React.Component <Props>{
handlerLinkBtnClick = (ev) => {
console.log(ev);
this.props.historyGo('./register');
handlerLinkBtnMouseMove = (ev) => {
console.log(ev);
render() {
return (
<header>
<p >This is Login Page </p>
<div className={styles.linkBtn}
onMouseMove={this.handlerLinkBtnMouseMove}
onClick={this.handlerLinkBtnClick}>
Go to Register Page
</header>
按住 Ctrl ,然后鼠标移动到事件名上就能获取当前事件处理函数的参数类型
image.png
https://juejin.im/post/5e2690dce51d454d310fb4ef
![]() |
谈吐大方的鸵鸟 · 华为adb彻底关闭系统更新-掘金 1 年前 |