翼度科技»论坛 编程开发 JavaScript 查看内容

如何手动补充陈年老库(或纯 JS 代码)的 TypeScript 类型?

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
目录

  
这篇仅为自己工作中在 js 和 ts 交叉斗智斗勇的实践中留下的经验,不一定完全、合理,仅供参考,有错漏难免,有则评论区指出。
前置知识 - JavaScript 的各种模块化情况



  • 全局模块,在 globalThis 上可以访问,一般是 iife 库程序

  • ES 模块

  • CommonJS 模块

前置知识2 - 让你写的 d.ts 在工程中生效



  • 确保当前工程目录中使用的 TypeScript 是 node_modules 下的开发依赖,快捷命令 Ctrl + Shift + P,选择 TypeScript 版本即可

  • 在 tsconfig.json 中配置 include 项,使得你写的 d.ts 文件在 include 的路径中即可

1. 全局模块的定义

假设我有一个定义在 globalThis 上的库,名叫 WebCC,它很简单:
  1. window.WebCC = (function(){
  2.   const foo = () => {
  3.     console.log('foo')   
  4.   }
  5.   const bar = 'bar'
  6.   const NAME = 'WebCC'
  7.   return {
  8.     foo,
  9.     bar,
  10.     NAME
  11.   }
  12. })()
复制代码
那么,它应该使用 namespace 来定义:
  1. declare namespace WebCC {
  2.   function foo(): void
  3.   const bar: string
  4.   const NAME: string
  5. }
复制代码
2. ES 模块的定义

仍以上述 WebCC 这个名字为例,但是这次是 ES 模块:
  1. // webcc.js
  2. export const bar = 'bar'
  3. export const NAME = 'WebCC'
  4. export const foo = () => {
  5.   console.log('foo')
  6. }
复制代码
那么,它应该使用 module 来定义:
  1. // webcc.d.ts
  2. declare module 'webcc' {
  3.   export const bar: string
  4.   export const NAME: string
  5.   export const foo: () => void
  6. }
复制代码
module 关键字后面的模块名即 import 时的模块名:
  1. import { foo } from 'webcc'
复制代码
2.1. 默认导出

  1. declare module 'webcc' {
  2.   const XXX: string
  3.   export default XXX
  4. }
复制代码
2.2. 导出类

  1. declare module 'webcc' {
  2.   export class Foo {
  3.     /** 构造器 */
  4.     constructor()
  5.     /** 字段成员,类型为函数 */
  6.     foo: () => void
  7.     /** 字段成员,类型为 string */
  8.     NAME: string
  9.     /** 函数成员 */
  10.     bar(): void
  11.     /** 静态字段成员,类型为 number */
  12.     static VERSION: number
  13.   }
  14. }
复制代码
2.3. 注意事项

在模块声明的 d.ts 文件中,想引入其他模块的定义,不能像模块一样使用 import 指令,而是要使用 import()。例如,想在 parser.d.ts 中引入别人已经定义好的数据类型,来自 @types/foo 的 Foo 类型,那么要写成:
  1. declare module 'my-parser' {
  2.   export parse(val: import('foo').Foo): string
  3. }
复制代码
这是因为一旦在代码文件顶部写了 import 就会被当作模块文件,而不是类型声明文件。这个特性来自 TS 2.9 版本。
3. CommonJS 模块定义

CommonJS 的模块声明与 ES 模块声明大同小异,即 module.exports.foo(或简写 exports.foo)对应 export foo,module.exports = foo 对应 export default foo。
3.1. 挨个导出

  1. module.exports = {
  2.   foo: function() {
  3.     console.log('foo')  
  4.   },
  5.   bar: "bar"
  6. }
复制代码
类型声明为:
  1. declare module 'webcc' {
  2.   export const foo: () => void
  3.   export const bar: string
  4. }
复制代码
3.2. 默认导出

  1. module.exports = class WebCC {
  2.   foo() {
  3.     console.log('foo')   
  4.   }
  5. }
复制代码
类型声明为:
  1. declare module 'webcc' {
  2.   export default class WebCC {
  3.     foo(): void
  4.   }
  5. }
复制代码
4. 声明类型(TypeScript 中的 interface 或 type)和其它

4.1. type 和 interface

满足前置知识2 的前提下,在任意 d.ts 中书写的 interface、type 定义均可被整个项目使用:
  1. declare type WebCCOptions = {
  2.   foo: string
  3.   bar: boolean
  4. }
  5. declare interface WebCCResponse {
  6.   foo: string
  7. }
复制代码
4.2. 全局变量(非 namespace)

全局变量也可以如法炮制:
  1. declare const WebCC: {
  2.   foo: () => void
  3. }
复制代码
4.3. 补充功能

例如,想为原生数组补充一个新的函数成员 foo,先在某些地方实现:
  1. // somefile.js
  2. Array.prototype.foo = function() {
  3.   console.log('foo')
  4. }
复制代码
这个时候需要补齐这个类型:
  1. // somefile.d.ts
  2. declare interface Array<T> {
  3.   foo(): void
  4. }
复制代码
有的读者可能不知道为什么 Array 是 interface,那是因为这个是官方的定义,我只是点了点 F12 ... 毕竟 interface 才能继续补充定义,官方的 d.ts 更完善、强大,建议自学。

来源:https://www.cnblogs.com/onsummer/p/17080178.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具