2023年6月20日发(作者:)

elementui的css⽂件没有引⼊_typescript声明⽂件加载机制以及在不同场景。。。. ⽂件是 typescript 的声明⽂件,主要⽤来给编辑器做代码提⽰⽤,具体的书写位置和⽅式根据你的具体需求⽽定。(嫌弃太长可以直接跳到使⽤⽅式的 1.1 和 2.3)很多初学者(⽐如我)刚开始接触 ts 时,⼀直分不清 .ts 和 . 的区别,不知道 . 存在的意义是什么。刚开始跟着各种教程搭建好了ts 开发环境,写好了 hello world 时,发现就算没有写 . ⽂件,编辑器(这⾥以及之后的编辑器均指宇宙第⼀编辑器 vscode )也可以获得代码提⽰,甚⾄就算写的不是 ts ⽽是 js,只要 import 的依赖关系明确,对于⼀些简单的函数依然能获得最基本的⼊参、返回值的形参提⽰。从⽽就想知道 . ⽂件存在的意义是什么。其实这个问题稍微思考⼀下就能知道。假设我们⽤ ts 开发了⼀个 npm 库,经过编译打包之后发布到了 npm 上,其他⽤户下载了我们这个库,下载到他本地的⼀般是⼀个 dist/ , ⾥的 main 指向这个 dist/ ⽂件。这时候不管这个⽤户开发使⽤的是 ts 还是 js,当他 import 我们这个库的时候都⽆法获得代码提⽰。. ⽂件主要是 for 第三⽅库,让第三⽅库的使⽤者可以获得良好的代码和接⼝提⽰。本⽂主要介绍 . ⽂件的加载机制以及在纯. ⽂件主要是 for 第三⽅库,让第三⽅库的使⽤者可以获得良好的代码和接⼝提⽰纯js开发环境中如何使⽤ . 声明⽂件,获得代码提⽰和接⼝声明。js加载机制⼀些定义:三斜线指令定义: /// 或者 /// 特点:在 .ts 中已经不再使⽤,但是在 . 中还是有⼀定⽤处只能出现在⽂件的最开头,并且前⾯只能有注释或者别的三斜线指令有点类似 C++ 的 #include,但tsc 不会把 xxx 的代码插⼊替换到三斜线指令的位置声明⽂件:定义:. 后缀的⽂件特点:⾥⾯不允许有任何函数的实现顶层作⽤域⾥只能出现 declare import export interface 三斜线指令全局类声明⽂件:定义:如果⼀个声明⽂件的顶层作⽤域顶层作⽤域中没有 import && export,那么这个声明⽂件就是⼀个全局类声明⽂件特点:如果⼀个全局类声明⽂件在 ts 处理范围内,如果⼀个全局类声明⽂件在 ts 处理范围内, 那么全局类声明⽂件中的 declare 会在全局⽣效模块类声明⽂件:定义:如果⼀个声明⽂件的顶层作⽤域顶层作⽤域中有 import || export,那么这个声明⽂件就是⼀个模块类声明⽂件特点:⾥⾯的 declare 不会在全局⽣效,需要按模块的⽅式导出来才能⽣效⼀些⾏为:ts 编译器会包含下⾯的所有 . ⽂件: 的 file (并集) include (差集) exclude对于 node_modules/@types 下的每个 npm 包,ts 会按照 node 解析包的那⼀套流程(如果有 并且⾥⾯有main 字段,将 main 字段的⽂件作为⼊⼝⽂件。如果上⾯流程失败,将 作为⼊⼝⽂件。否则抛出错误)。这个⼊⼝⽂件⾥⾯的三斜线指令、import 所引⼊的其他⽂件,会按照其他⽂件⾃⼰的规则⽣效其他⽂件⾃⼰的规则⽣效// node_modules/@types/my//// import "./b";

import c from "./c";

declare const index = 1;

export default index;// node_modules/@types/my/are const a = 1;// node_modules/@types/my/are const b = 1;// node_modules/@types/my/are const c = 1;export default c;上⾯的代码的效果为:全局:a、b⾮全局: index、cindex ⽂件中,由于含有 import export,所以为模块类声明⽂件,⾥⾯使⽤ declare 声明的 index 为⾮全局,在被使⽤时只有 importindex from 'my';时才能拿到 index。⽽引⼊的 a、b ⽂件,这两个⽂件虽然是被 index 这个模块类声明⽂件引⼊的,但是这两个⽂件⾃⼰本⾝是全局类声明⽂件(既没有 import 也没有 export),所以这两个⽂件⾥⾯ declare 的变量都可以在全局访问到。⽽引⼊的 c ⽂件⾥⾯含有 export,所以为模块⽂件,⾥⾯的 c ⽆法在全局被访问。FAQ | Tips | 注意事项:1. 既然 a、b ⽂件为全局类声明⽂件,那么为什么还要在 index 中引⼊?因为⼀般情况下 的 exclude 会加⼊ node_modules,所以理论上 node_modules ⾥⾯的所有⽂件都不会被 ts 编译。⽽node_modules/@types ⽐较特别,⾥⾯的每个包会被作为⼀个模块,这个模块只会有⼀个⼊⼝⽂件(⽐如默认的 )。这个⼊⼝⽂件中没有引⼊的,都不会被 ts 处理。所以上⾯的代码在 index ⽂件中去掉【import "./b";】 之后,b ⽂件中的【declare const b =1;】不会被 ts 看到(a 同理)。2. import 和 /// 的区别是什么?主要还是历史遗留问题,三斜线指令出现的时候 ES6 还没出来。三斜线指令不会将⼀个全局⽂件变成模块⽂件,⽽ import 会。如果你需要⼀个在⼀个全局⽂件 b ⾥⽤另⼀个⽂件 c ⾥的变量,就可以⽤三斜线指令,因为⽤ import 会把 b 变成⼀个模块⽂件。3. 我想在⼀个模块⽂件⾥导出全局变量怎么办?

这种情况只能导出⼀个 namespace:export const d = 1;

export as namespace whateverthisis; // 全局 namespace 名,whateverthisis.d 就可以访问到(export as namespace 只能在模块⽂件⾥⾯使⽤)4. vscode 的 ts 代码提⽰的缓存机制vscode 的 ts 代码提⽰会缓存 node_modules/@types 下的每个 package 的⼊⼝⽂件地址( 的 main 字段)。如果⼿动去改变了某个包的 main 字段,改成了另⼀个⽂件,那么在重启 ts server(vs code⾃带的⼀个东西),修改是不会⽣效的,⼊⼝⽂件依旧是以前的⼊⼝⽂件(但是去修改⼊⼝⽂件⾥的内容是可以⽣效的)5. declare module A 和 declare module 'a' 的区别前者已经被废弃,使⽤ declare namespace A代替;后者⽤于扩展⼀个已有的模块 a。全局⽂件下的 declare module 'xx' 会在全局环境⽣成⼀个名为 xx 的模块,并且可以在⾥⾯定义这个 xx 模块应该有的导出,⼀般⽤来添加或补充 node_modules 中的模块的声明⽂件。同名的 declare module ⾥⾯的导出会合并。使⽤⽅式常⽤的是下⾯的 1.11.1 、2.32.31. 添加全局的代码提⽰(直接输⼊变量即可获得代码提⽰)1.1 添加⾃定义全局变量场景:全局注⼊变量,⽐如⼩程序的 Page正确操作:正确操作// (简化版)interface Opt { data: D,

}

declare function Page(opt:Opt):void;1.2 添加第三⽅库的全局代码提⽰场景:添加 jquery、lodash 等全局变量在简单 web 页⾯的场景下,经常是直接新建⼀个 ,在⾥⾯⽤ script 标签引⼊ jquery lodash 的外部 CDN。然后新建⼀个,在 中⽤ script 标签引⼊ ,这样在 ⾥是可以直接使⽤ $ _ 这两个变量的,但是输⼊ $ _ 时⽆法获得代码提⽰。 (image)如果获得了代码提⽰,⼤概率是 ts 的缓存⽬录⽣效了:~/Library/Caches/typescript/3.8 ⾥⾯可能有曾经下载过的 npm 包。不过⼀个正常的 ts 项⽬⼀般根⽬录会有 ,⾥⾯的 include ⾥可以规定需要 ts 编译的⽬录,这个字段填写好了之后 ts 就不会去缓存⽬录⾥找了,除⾮你把缓存⽬录也写进去。正确操作: 正确操作npm i @types/jquery @types/lodash -D这样会得到 node_modules/@types/jquery 和 node_modules/@types/lodash 两个⽬录。ts 会检查 node_modules/@types 下⾯的所有 . ⽂件,所以编辑器就获得了 jQuery $ _ 三个全局变量的代码提⽰。2. 添加局部的模块代码提⽰2.1 扩展挂载到全局的模块的代码提⽰场景1:给 ype 加⼀个⽅法虽然这种⾏为不被推荐,但是某些情况下你可能的确需要这么做。⽐如现在给 ype 加了⼀个 getSum ⽅法,获取数组中所有元素的和:// somewhere_ = function(){

return ((result, value) => result + value, 0);

}正确操作:正确操作// rface Array {

getSum(): T extends number ? number : void;

}场景2:给 jQuery 加⼀个静态⽅法 $.getHelloWorld通过简单分析(opt+click)可以知道,暴露在全局的 jQuery 和 $ 本⾝是两个全局的 const 常量,类型是 JQueryStatic,所以只需要给这个 JQueryStatic 接⼝增加 getHelloWorld ⽅法就可以了。正确操作:正确操作// rface JQueryStatic {

getHelloWorld(): string;

}场景3:给 lodash 加⼀个静态⽅法 _.getHelloWorld通过简单分析(opt+click)可以知道,暴露在全局的 _ 本⾝是⼀个 const 变量,类型为 _.LoDashStatic,但是这个 _.LoDashStatic 并没有被暴露到全局,所以需要使⽤ declare module 的语法来 override lodash 这个模块正确操作:正确操作// (和 分开,否则会使 中的 declare 失去全局性)import _ from 'lodash'; // 注意这个 import 必须写在 declare module 外部

declare module 'lodash' { interface LoDashStatic { getHelloWorld(): string;

}

}2.2 扩展⾮全局模块,但⾃带声明⽂件的模块(esm、commonjs)场景:node 的 fs 增加⼀个 getHelloWorld ⽅法先通过npm i @types/node -D安装 node 的声明⽂件正确操作:正确操作// are module 'fs' {

export function getHelloWorld(): string;

}2.3 扩展⾮全局模块,并且不⾃带声明⽂件的模块场景1:你从 npm 上⾯下载了⼀个 ex-module 模块,但是这个作者很懒,没有提供声明⽂件,@types 社区也没有⼈提供,你想⾃⼰给 ex-module 写声明⽂件,便于后续开发假设 ex-module 长这样:// node_modules/ex-module/rt _ from 'lodash';

export function add(a, b) { return a + b; }

export function minus(a, b) { return a - b; }

export default function getLodash() { return _; }正确操作:正确操作// are module 'ex-module' {

import { LoDashStatic } from 'lodash'; // 注意这个 import 必须写在 declare module 内部

export function add(): number;

export function minus(): number;

export default function getLodash(): LoDashStatic;

}// rt getLodash,{ add, minus } from 'ex-module';add(1, 130);坑点:由于 js + ts 模块化过于混乱,各⾃的实现也有冲突,建议全部严格按照 ES6 的⽅式来写⽐如上⾯的 中,如果删掉 export default 宇航,在 中 import ex from 'ex-module',输⼊ ex 时候, vscode 会提⽰ 是⼀个函数,但实际上 ex 是 undefined场景2:你⽤ js 给⾃⼰写了⼀个 util 库,⾥⾯有各种各样的⼯具函数,想给他们加上声明⽂件// util/rt function add(a, b) {

return a + b;

}

export function minus(a, b) {

return a - b;

}正确操作:正确操作// util/rt function add(a: number, b: number): number;

export function minus(a: number, b: number): number;总结: ts 由于各种复杂的历史遗留问题,模块⽅⾯⽐较混乱,坑也很多,还是建议统⼀⽤ ES6 模块

2023年6月20日发(作者:)

elementui的css⽂件没有引⼊_typescript声明⽂件加载机制以及在不同场景。。。. ⽂件是 typescript 的声明⽂件,主要⽤来给编辑器做代码提⽰⽤,具体的书写位置和⽅式根据你的具体需求⽽定。(嫌弃太长可以直接跳到使⽤⽅式的 1.1 和 2.3)很多初学者(⽐如我)刚开始接触 ts 时,⼀直分不清 .ts 和 . 的区别,不知道 . 存在的意义是什么。刚开始跟着各种教程搭建好了ts 开发环境,写好了 hello world 时,发现就算没有写 . ⽂件,编辑器(这⾥以及之后的编辑器均指宇宙第⼀编辑器 vscode )也可以获得代码提⽰,甚⾄就算写的不是 ts ⽽是 js,只要 import 的依赖关系明确,对于⼀些简单的函数依然能获得最基本的⼊参、返回值的形参提⽰。从⽽就想知道 . ⽂件存在的意义是什么。其实这个问题稍微思考⼀下就能知道。假设我们⽤ ts 开发了⼀个 npm 库,经过编译打包之后发布到了 npm 上,其他⽤户下载了我们这个库,下载到他本地的⼀般是⼀个 dist/ , ⾥的 main 指向这个 dist/ ⽂件。这时候不管这个⽤户开发使⽤的是 ts 还是 js,当他 import 我们这个库的时候都⽆法获得代码提⽰。. ⽂件主要是 for 第三⽅库,让第三⽅库的使⽤者可以获得良好的代码和接⼝提⽰。本⽂主要介绍 . ⽂件的加载机制以及在纯. ⽂件主要是 for 第三⽅库,让第三⽅库的使⽤者可以获得良好的代码和接⼝提⽰纯js开发环境中如何使⽤ . 声明⽂件,获得代码提⽰和接⼝声明。js加载机制⼀些定义:三斜线指令定义: /// 或者 /// 特点:在 .ts 中已经不再使⽤,但是在 . 中还是有⼀定⽤处只能出现在⽂件的最开头,并且前⾯只能有注释或者别的三斜线指令有点类似 C++ 的 #include,但tsc 不会把 xxx 的代码插⼊替换到三斜线指令的位置声明⽂件:定义:. 后缀的⽂件特点:⾥⾯不允许有任何函数的实现顶层作⽤域⾥只能出现 declare import export interface 三斜线指令全局类声明⽂件:定义:如果⼀个声明⽂件的顶层作⽤域顶层作⽤域中没有 import && export,那么这个声明⽂件就是⼀个全局类声明⽂件特点:如果⼀个全局类声明⽂件在 ts 处理范围内,如果⼀个全局类声明⽂件在 ts 处理范围内, 那么全局类声明⽂件中的 declare 会在全局⽣效模块类声明⽂件:定义:如果⼀个声明⽂件的顶层作⽤域顶层作⽤域中有 import || export,那么这个声明⽂件就是⼀个模块类声明⽂件特点:⾥⾯的 declare 不会在全局⽣效,需要按模块的⽅式导出来才能⽣效⼀些⾏为:ts 编译器会包含下⾯的所有 . ⽂件: 的 file (并集) include (差集) exclude对于 node_modules/@types 下的每个 npm 包,ts 会按照 node 解析包的那⼀套流程(如果有 并且⾥⾯有main 字段,将 main 字段的⽂件作为⼊⼝⽂件。如果上⾯流程失败,将 作为⼊⼝⽂件。否则抛出错误)。这个⼊⼝⽂件⾥⾯的三斜线指令、import 所引⼊的其他⽂件,会按照其他⽂件⾃⼰的规则⽣效其他⽂件⾃⼰的规则⽣效// node_modules/@types/my//// import "./b";

import c from "./c";

declare const index = 1;

export default index;// node_modules/@types/my/are const a = 1;// node_modules/@types/my/are const b = 1;// node_modules/@types/my/are const c = 1;export default c;上⾯的代码的效果为:全局:a、b⾮全局: index、cindex ⽂件中,由于含有 import export,所以为模块类声明⽂件,⾥⾯使⽤ declare 声明的 index 为⾮全局,在被使⽤时只有 importindex from 'my';时才能拿到 index。⽽引⼊的 a、b ⽂件,这两个⽂件虽然是被 index 这个模块类声明⽂件引⼊的,但是这两个⽂件⾃⼰本⾝是全局类声明⽂件(既没有 import 也没有 export),所以这两个⽂件⾥⾯ declare 的变量都可以在全局访问到。⽽引⼊的 c ⽂件⾥⾯含有 export,所以为模块⽂件,⾥⾯的 c ⽆法在全局被访问。FAQ | Tips | 注意事项:1. 既然 a、b ⽂件为全局类声明⽂件,那么为什么还要在 index 中引⼊?因为⼀般情况下 的 exclude 会加⼊ node_modules,所以理论上 node_modules ⾥⾯的所有⽂件都不会被 ts 编译。⽽node_modules/@types ⽐较特别,⾥⾯的每个包会被作为⼀个模块,这个模块只会有⼀个⼊⼝⽂件(⽐如默认的 )。这个⼊⼝⽂件中没有引⼊的,都不会被 ts 处理。所以上⾯的代码在 index ⽂件中去掉【import "./b";】 之后,b ⽂件中的【declare const b =1;】不会被 ts 看到(a 同理)。2. import 和 /// 的区别是什么?主要还是历史遗留问题,三斜线指令出现的时候 ES6 还没出来。三斜线指令不会将⼀个全局⽂件变成模块⽂件,⽽ import 会。如果你需要⼀个在⼀个全局⽂件 b ⾥⽤另⼀个⽂件 c ⾥的变量,就可以⽤三斜线指令,因为⽤ import 会把 b 变成⼀个模块⽂件。3. 我想在⼀个模块⽂件⾥导出全局变量怎么办?

这种情况只能导出⼀个 namespace:export const d = 1;

export as namespace whateverthisis; // 全局 namespace 名,whateverthisis.d 就可以访问到(export as namespace 只能在模块⽂件⾥⾯使⽤)4. vscode 的 ts 代码提⽰的缓存机制vscode 的 ts 代码提⽰会缓存 node_modules/@types 下的每个 package 的⼊⼝⽂件地址( 的 main 字段)。如果⼿动去改变了某个包的 main 字段,改成了另⼀个⽂件,那么在重启 ts server(vs code⾃带的⼀个东西),修改是不会⽣效的,⼊⼝⽂件依旧是以前的⼊⼝⽂件(但是去修改⼊⼝⽂件⾥的内容是可以⽣效的)5. declare module A 和 declare module 'a' 的区别前者已经被废弃,使⽤ declare namespace A代替;后者⽤于扩展⼀个已有的模块 a。全局⽂件下的 declare module 'xx' 会在全局环境⽣成⼀个名为 xx 的模块,并且可以在⾥⾯定义这个 xx 模块应该有的导出,⼀般⽤来添加或补充 node_modules 中的模块的声明⽂件。同名的 declare module ⾥⾯的导出会合并。使⽤⽅式常⽤的是下⾯的 1.11.1 、2.32.31. 添加全局的代码提⽰(直接输⼊变量即可获得代码提⽰)1.1 添加⾃定义全局变量场景:全局注⼊变量,⽐如⼩程序的 Page正确操作:正确操作// (简化版)interface Opt { data: D,

}

declare function Page(opt:Opt):void;1.2 添加第三⽅库的全局代码提⽰场景:添加 jquery、lodash 等全局变量在简单 web 页⾯的场景下,经常是直接新建⼀个 ,在⾥⾯⽤ script 标签引⼊ jquery lodash 的外部 CDN。然后新建⼀个,在 中⽤ script 标签引⼊ ,这样在 ⾥是可以直接使⽤ $ _ 这两个变量的,但是输⼊ $ _ 时⽆法获得代码提⽰。 (image)如果获得了代码提⽰,⼤概率是 ts 的缓存⽬录⽣效了:~/Library/Caches/typescript/3.8 ⾥⾯可能有曾经下载过的 npm 包。不过⼀个正常的 ts 项⽬⼀般根⽬录会有 ,⾥⾯的 include ⾥可以规定需要 ts 编译的⽬录,这个字段填写好了之后 ts 就不会去缓存⽬录⾥找了,除⾮你把缓存⽬录也写进去。正确操作: 正确操作npm i @types/jquery @types/lodash -D这样会得到 node_modules/@types/jquery 和 node_modules/@types/lodash 两个⽬录。ts 会检查 node_modules/@types 下⾯的所有 . ⽂件,所以编辑器就获得了 jQuery $ _ 三个全局变量的代码提⽰。2. 添加局部的模块代码提⽰2.1 扩展挂载到全局的模块的代码提⽰场景1:给 ype 加⼀个⽅法虽然这种⾏为不被推荐,但是某些情况下你可能的确需要这么做。⽐如现在给 ype 加了⼀个 getSum ⽅法,获取数组中所有元素的和:// somewhere_ = function(){

return ((result, value) => result + value, 0);

}正确操作:正确操作// rface Array {

getSum(): T extends number ? number : void;

}场景2:给 jQuery 加⼀个静态⽅法 $.getHelloWorld通过简单分析(opt+click)可以知道,暴露在全局的 jQuery 和 $ 本⾝是两个全局的 const 常量,类型是 JQueryStatic,所以只需要给这个 JQueryStatic 接⼝增加 getHelloWorld ⽅法就可以了。正确操作:正确操作// rface JQueryStatic {

getHelloWorld(): string;

}场景3:给 lodash 加⼀个静态⽅法 _.getHelloWorld通过简单分析(opt+click)可以知道,暴露在全局的 _ 本⾝是⼀个 const 变量,类型为 _.LoDashStatic,但是这个 _.LoDashStatic 并没有被暴露到全局,所以需要使⽤ declare module 的语法来 override lodash 这个模块正确操作:正确操作// (和 分开,否则会使 中的 declare 失去全局性)import _ from 'lodash'; // 注意这个 import 必须写在 declare module 外部

declare module 'lodash' { interface LoDashStatic { getHelloWorld(): string;

}

}2.2 扩展⾮全局模块,但⾃带声明⽂件的模块(esm、commonjs)场景:node 的 fs 增加⼀个 getHelloWorld ⽅法先通过npm i @types/node -D安装 node 的声明⽂件正确操作:正确操作// are module 'fs' {

export function getHelloWorld(): string;

}2.3 扩展⾮全局模块,并且不⾃带声明⽂件的模块场景1:你从 npm 上⾯下载了⼀个 ex-module 模块,但是这个作者很懒,没有提供声明⽂件,@types 社区也没有⼈提供,你想⾃⼰给 ex-module 写声明⽂件,便于后续开发假设 ex-module 长这样:// node_modules/ex-module/rt _ from 'lodash';

export function add(a, b) { return a + b; }

export function minus(a, b) { return a - b; }

export default function getLodash() { return _; }正确操作:正确操作// are module 'ex-module' {

import { LoDashStatic } from 'lodash'; // 注意这个 import 必须写在 declare module 内部

export function add(): number;

export function minus(): number;

export default function getLodash(): LoDashStatic;

}// rt getLodash,{ add, minus } from 'ex-module';add(1, 130);坑点:由于 js + ts 模块化过于混乱,各⾃的实现也有冲突,建议全部严格按照 ES6 的⽅式来写⽐如上⾯的 中,如果删掉 export default 宇航,在 中 import ex from 'ex-module',输⼊ ex 时候, vscode 会提⽰ 是⼀个函数,但实际上 ex 是 undefined场景2:你⽤ js 给⾃⼰写了⼀个 util 库,⾥⾯有各种各样的⼯具函数,想给他们加上声明⽂件// util/rt function add(a, b) {

return a + b;

}

export function minus(a, b) {

return a - b;

}正确操作:正确操作// util/rt function add(a: number, b: number): number;

export function minus(a: number, b: number): number;总结: ts 由于各种复杂的历史遗留问题,模块⽅⾯⽐较混乱,坑也很多,还是建议统⼀⽤ ES6 模块