TypeScript 中的声明文件

参考:html

TypeScript学习笔记

TypeScript 中的声明文件

tsconfig.json配置说明

【进阶】TS高级类型,泛型

TSlint注释忽略错误和RESTful理解

关闭Eslint语法校验

详解 ESLint 规则,规范你的代码

学习 TypeScript 稍微有一段时间了,每次写都会碰到有关声明文件的问题,目前为止暂未彻底搞清楚,在此记录一些相关问题,之后碰到可以迅速解决。node

声明文件(x.d.ts)

TypeScript 做为 JavaScript 的超集,在开发过程当中不可避免要引用其余第三方的 JavaScript 的库。虽然经过直接引用能够调用库的类和方法,可是却没法使用TypeScript 诸如类型检查等特性功能。为了解决这个问题,须要将这些库里的函数和方法体去掉后只保留导出类型声明,而产生了一个描述 JavaScript 库和模块信息的声明文件。经过引用这个声明文件,就能够借用 TypeScript 的各类特性来使用库文件了。react

在开始描述各类问题以前,列举一下我所知道的声明文件存放的方式(常规配置下):webpack

  1. src/@types/,在 src 目录新建@types目录,在其中编写.d.ts声明文件,声明文件会自动被识别,能够在此为一些没有声明文件的模块编写本身的声明文件; 2018-10-31:实际上在 tsconfig``include 字段包含的范围内编写 .d.ts,都将被自动识别。
  2. x.js相同目录建立同名声明文件x.d.ts,这样也会被自动识别;
  3. node_modules/@types/下存放各个第三方模块的声明文件,经过yarn add @types/react自动下载到此处,本身编写的声明文件不要放在这里;
  4. 做为 npm 模块发布时,声明文件可捆绑发布,需在package.json中指明"types": "./types/index.d.ts"
  5. typings 声明管理器,了解很少,已经不推荐使用;

隐式 any 类型(implicitly has an ‘any’ type)

当 tsconfig.json 中关闭"noImplicitAny": false时,能够直接在 TypeScript 中引用 JavaScript(无声明文件)的库,全部的引入都会被默认为any类型。但为了规范编码,老是打开"noImplicitAny": true,这样当发生上述状况时,编译器会阻止编译,提示咱们去加上类型规范。git

TS 中导入 JS

// hello.js
    exportconst hello = () =>console.log('hello')
    
    // index.ts
    import {hello} from'./hello'
    // 若是使用 vscode,编辑器就会给出错误提示:
    // [ts] 没法找到模块“./hello”的声明文件。“src/hello.js”隐式拥有 "any" 类型。
    hello()
    
    // 若是执行编译,控制台也会给出一样的错误提示:
    // Could not find a declaration file for module './hello'. 'src/hello.js' implicitly has an 'any' type.
复制代码

这就告诉咱们,若要在index.ts中使用hello.js,须要先为hello.js编写声明文件。github

关于 declareweb

// hello.d.ts
    // 描述 hello.js
    export declare const hello: () =>void
复制代码

另外一种书写方式,目前还没彻底搞清二者本质区别:typescript

// hello.d.ts
    exportas namespace hello;
    
    export = hello;
    
    declare namespace hello {
        exportconst hello: () =>void;
    }
复制代码

实际上,看了一些第三方模块的声明文件,形式也是五花八门,看得一头雾水,学得一头包……npm

TS 中导入 .png、.json 等

不止是在 TypeScript 中导入未声明 JavaScript,导入.png.json等文件时也一样须要去编写声明文件。json

提供一种方式,能够建立一个声明文件src/@types/definition.d.ts(你也能够命名为其余),在其中编写以下声明:

// definition.d.ts
    declare module'*.png' {
        const value: string
        export = value
    }
    
    // index.ts
    // 以后在 TS 中导入也不会有问题
    import avatar from'./img/avatar.png'
复制代码

或者可使用require

const avatar = require('./img/avatar.png')
    // 可能会提示 require 未定义,有两种方式:
    //  1. 自行声明:declare const require: any
    //  2. yarn add -D @types/node
复制代码

第三方模块没有可用的声明文件

通常使用第三方不是 TypeScript 编写的模块时,咱们能够直接下载对应的声明文件:yarn add @types/{模块名}。然而有些模块是没有对应的声明文件的,这时候就须要咱们本身编写声明文件,以rc-form为例,只需在src/@types/definition.d.ts中添加对应代码便可:

// definition.d.ts
    declare module'*.png' {
        const value: string
        export = value
    }
    
    // 新增部分
    declare module"rc-form" {
        // 在此只是简单地进行类型描述
        exportconst createForm: any;
        exportconst createFormField: any;
        exportconst formShape: any;
    }
    
复制代码

webpack 别名(aliases)

当 webpack 中配置了别名,在 TS 中使用时会出现找不到模块

// webpack.config.js
    const config = {
         // ...
         aliases: {
         // 公共的工具类、容器和组件
         utils: path.resolve('../utils'),
      },
        // ...
    }
    
    // index.ts
    import {ua} from'utils/broswer'
    // Cannot find module 'utils/browser'
复制代码

只需在 tsconfig.json 添加baseUrlpaths的配置便可:

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "noImplicitAny": true,
    // 添加配置
    "baseUrl": ".",
    "paths": {
      "utils/*": ["../utils/*"],
      "components/*": ["../components/*"]
    }
  },
  "include": ["./src/*", "./src/**/*"],
  "exclude": ["node_modules"]
}
复制代码

类型“Window”上不存在属性“X”

有时候,直接经过 script、src 引入的对象在 window.X 是能够直接访问的,但在 TS 中直接使用时会提示不存在相应属性(The property ‘X’ does not exist on value of type ‘window’),这时候须要对 window 进行扩展,直接在src/@types/definition.d.ts中扩展。

// definition.d.ts
    interface Window {
      X: any
    }
    
    // index.ts
    console.log(window.X) // success
复制代码

我在使用时,想复用一些类型,从其余文件导入了一些内容,这时候出现了问题:

// definition.d.ts
    import {IPerson} from'./interfaces/index.ts'
    
    // ...
    
    interface Window {
      X: any
    }
    
    
    // index.ts
    console.log(window.X) // fail: 类型“Window”上不存在属性“X”
复制代码

而后发现,套一层global又能恢复正常,但没有import语句时,使用declare global会提示错误:

// definition.d.ts
    import {IPerson} from'./interfaces/index.ts'
    
    // ...
    
    // global 包裹
    declare global {
      interface Window {
        X: any
      }
    }
    
    
    // index.ts
    console.log(window.X) // success
复制代码

typescript 类型映射 (ReadOnly、Partial)

有时候须要一个类型,是依赖于上一个类型可是,对属性的要求去不一样

interface Person{
    name: string;
    agent: number;
}
type Person2 = Readonly<Person>;
type Person3 = Partial<Person>;
class Test {
    run() {
        let person: Person = {
            name: 'dd',
            agent: 1
        };
        person.name = 'cc';
        let person2: Person2 = {
            name: 'read',
            agent: 1
        };
        // person2.agent = 3; 报错
        let person3: Person3 = {
            name: 'person 3' // 属性不完整也不会报错
        }
    }
}

复制代码

ReadOnly、Partial源码

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}
复制代码

其实这些主要在定义是灵活应用 in, keyof便可实现

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
type Record<K extends string, T> = {
    [P in K]: T;
}
复制代码
相关文章
相关标签/搜索