声明合并

介绍

TypeScript 中的一些独特概念在类型级别描述了 JavaScript 对象的形状。TypeScript 的一个特别独特的例子是 'declaration merging' 的概念。在使用现有 JavaScript 时,理解这个概念会给你带来优势。它还为更高级的抽象概念打开了大门。

就本文而言,"declaration merging" 意味着编译器将使用相同名称声明的两个单独声明合并到一个定义中。这个合并的定义具有两个原始声明的特征。可以合并任意数量的声明;它不仅限于两个声明。

基本概念

在 TypeScript 中,声明至少在以下三个组之一中创建实体:命名空间、类型或值。命名空间创建声明创建一个命名空间,其中包含使用点分符号访问的名称。类型创建声明就是这样做的:它们创建一个类型,该类型在声明的形状中可见并绑定到给定名称。最后,创建值的声明创建在输出 JavaScript 中可见的值。

声明类型命名空间类型
命名空间XX
XX
枚举XX
接口X
类型别名X
功能X
多变的X

了解每个声明创建的内容将帮助您了解执行声明合并时合并的内容。

合并接口

最简单,也许是最常见的声明合并类型是接口合并。在最基本的层面上,合并将两个声明的成员机械地连接到一个同名的接口中。

interface Box {
  height: number;
  width: number;
}

interface Box {
  scale: number;
}

let box: Box = { height: 5, width: 6, scale: 10 };

eaNWXQy0Tl/UR6q+MMdXQqmB8z64Lc2H3iYKKW8mKWywS8LoyPCyI5mmLt+VSBjSnbEeWMCroZW7b1Qm7hwfZtTnI1FXPBYIiw0PQuYnOXg4FGNfwzM0e6rlwxxUyVtBrMKb4O8nnD3Nd8BuOmAYWpuYbR0ZBfCHzQZOOgLq6nRkt0O5ZTkhh+mHmnpR2B7BxtXlBFEbd+GGe0uBJZooylXzM6Mv/2RNL2RghilA+L1roaSsImvNq7JsMg2EHZPY4rHOHrFY5xwx9DvLaTlaLA==

SAUWJY834V3SbHGKMGMUOm8VMQnb8R6JELK/fhNr+Zm73qmNWfJrpoyoVJtXZaHCrSD/pTbJZjvJGK2cDw94dDl98Ncs/MTwnqpTwW3U7N29e8zg1q8CDCqNhQb1Ty1XOSIQPn2T6rm93JMI9eerhgvkbsfOA5cpUEKf76jbyXEaC38HDE3GWS4SK2yHOh6BPPEZu+C9LATlXfmrEJX8gSQb/neABZ1CJQx1mNTQCGuoor2d8BTCoNRLw5YL0Mb++2Aa63UOEL/kMKLxSwBb7B8l/IrPYzF/ycAc15Li2dEuzr4b3OAyUnMam8r7XULmc7K68Mi5EILHWPFBFe7oIg==

VdVbYaxc82FSWU3o8QXXv3N8k7+MiEBeG27X4YfOa8Y=

interface Cloner {
  clone(animal: Animal): Animal;
}

interface Cloner {
  clone(animal: Sheep): Sheep;
}

interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
}

ouy4NSrNLGGDBpwUjh17GVwdXEGD/1BuHe5Pn1vmXXiGgkKFxjYYD1cxE97GB59oGeOdI+15T+fK2IfQ0ftzmg==

interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
  clone(animal: Sheep): Sheep;
  clone(animal: Animal): Animal;
}

5ETuJNpYQfLMdjw8fwROdZd8mNIU6byrcNVIKyZ8pBbbovdvcyY0s4XxMjUXAhDHBNrwmPby7/Q2g8PH8KfcE8zxGHDNf0J0ayytxorOQFDTIb6D18tzKqhP/nV1qiLNnDxrsHM7XMB8wRegF14Qgw==

azUSx+8MTU/eyg3WO7+ca0QvJqEa/hu0BCndgqpG0Eb0MLiL9huJv+0yRLZTZLe6uM93VQhW/Nn48bKdmECTeEH7MatzFYSYI1pEGx+DhRM1HqHWObjv+T/ylXPhJhlbEDiQBHz9PC4pB8oDvL7Z2WdcugKhFZ1e6Y/lnssjEgWOdIwL9/3nIJtd3ifWeAJAUqYhst2eBeX/lkl2dhcmQpfbMeAqw68Drc4e5H7p/aWAukEbWpl6AxghtbcnquOjOk1omnX9qDuT6qvwLBmcBg==

DFfo+jv/1wWtt7R0ye5jWqr5NfU91HY5gRXKkbnJ9vnoxOHg9/R5lS/iw6in952V

interface Document {
  createElement(tagName: any): Element;
}
interface Document {
  createElement(tagName: "div"): HTMLDivElement;
  createElement(tagName: "span"): HTMLSpanElement;
}
interface Document {
  createElement(tagName: string): HTMLElement;
  createElement(tagName: "canvas"): HTMLCanvasElement;
}

Yl6FRkVGwwkVUK0Kr5aGjxxpfYpQxpci1m490R1K8kADa9zJ7XIo7Ta5lRU4Qc4zEKdw/0CZdP8QZBIa+pGKaw==

interface Document {
  createElement(tagName: "canvas"): HTMLCanvasElement;
  createElement(tagName: "div"): HTMLDivElement;
  createElement(tagName: "span"): HTMLSpanElement;
  createElement(tagName: string): HTMLElement;
  createElement(tagName: any): Element;
}

合并命名空间

pwy09MypuVYB5s5IV+H7z5pyrp/+10JRyGyInytw1hr4+FcAzG9PrgTfMvQev0I0d815N+g27KDhW5De6M0/TMoudbtZ+Dmdyd+7roDHZI/B8BQlYhyvQRMB3lDKglJ71pwWTAPIuyxNMB6N4+0FFwvLGWNtTk98zFO/IaLC7/F+qdl9yS/FRIEufOHOaZoioU8qvwWXCPF+P2Wmre9M3g==

GesZ5/t/oA9RHio3+n66h0YAW7/asNILhSW1TbbjwVIzc7W4GVGaImzWguwzNdn29Qf1Dz/Zhv+EpKW9GBbjuRHewJigsS6k+0qaOs2BxKfTksPJRvAVpruXVALvY4MXfNckdVk8IGiIotxXx51GA5WDsJNzEY+2HzRSrXw/x1F/7EJTdhonXxxDQYPsHNcLT/3SbOAZhDa7jD8hHYZ6pv6Nthn8//d3drDgR1MSqBnfQz6ntdTwlftmfGAJRvlq

GesZ5/t/oA9RHio3+n66hxzJQrO0Ly7OsDUr0vrNthzOPqz6IXRFT6ic8L4qqT/ZzR35OeZseFayscAWWGOaYuEv+wCGRPARy9+RX1Ohg7gnpxqFpsC3EVAyRFU+CnTWm57ugBn12DJz9fZWt7nc98VuC12yYR9HeGCZVuyV6gXFFW/QgK+XbmvGO1jbl2Adb707Mryi6iXq/tORlIKHKh2zAztKHs0AOMdqV+aTHREry/f+INccjev+7VHtYVWqcogJfL70+y5sC3g3nudbCrqAh6P6nqrT1/gY5Y7mCWA/27eoyJ86ebq8XeB6QTSl

yGOsDW/oXPgms7xiTgc7b/vNYx9FItH6KLnkvcqTPOfjKIxbwTRDUgmIKz6GLObw

namespace Animals {
  export class Zebra {}
}

namespace Animals {
  export interface Legged {
    numberOfLegs: number;
  }
  export class Dog {}
}

BRW8qFL8hWjO9fL/tDytBg==

namespace Animals {
  export interface Legged {
    numberOfLegs: number;
  }

  export class Zebra {}
  export class Dog {}
}

qh8ff5sAWTjwNtW9xmvSjRQmszQbUflWOkx6nanCZ8u7NjCJw8pid7pNtdnsrFZz/kKfN7F7d+7rTwHoidFR7KDCkHO8dd7pQuyPy6AtDa6Xa6oAPdkkO7fXrErjO6ZXBdPrzSF6EnCDjYajN9lJBlP80QiLw6uNQSQjnKebw2rRnFVvCDqoIHHqPhvklZUuSjazrmLIHnMehOmq9EP6lycIatyrIYMgqMAJgWtctTzbz5AmaQRrPcke/yXoCc0cDxvyAGZjpNVtjp6Y2Hg5RuwCF1r07j9gpxjp3Cp50+xo/mLbLegr9Js12t0R15MKOfz8qbmcZ82puojJ6BNNl4RdHXzI7FMpeUxohiN11+g=

Imq5rcoqgeuM5r4yD6csoRXa98SrlmSjICfonI358WkyWlErpxLXuiYc6JzG+PVVryYy1V53F7VDqgZa3hegYg==

namespace Animal {
  let haveMuscles = true;

  export function animalsHaveMuscles() {
    return haveMuscles;
  }
}

namespace Animal {
  export function doAnimalsHaveMuscles() {
    return haveMuscles; // Error, because haveMuscles is not accessible here
  }
}

L3r4oEcohxQLMCQt/2QtN6w4MhVLdjHRds7tV7/ySOhp5aEZEAbl28sJoIkkAxCAE1DWPLlSzPYrTjQQ08EQyRDQIgDD5qEk8A3wEGJBHJHT9S2B/fYRwWBQRjq9G7Zp8hmZxzD/4UAil1rb2+Bc/+bEqva5JotzM4T9zlEL4tWusEnoC76MrPH5rE0gR0yorwoHOGbTLwJTkdo01TPDbJa9tkkr/vLW4z3SSygXm9XdKMPBgABRnTSdU6EoLs8flbLyRUikJZHPnge7sq0bdVeIV9y/l6RIX3vX3tfrUythjILZ8xX/hiHN4YHLdmDXajx5lmgPwgPyFV6qRhFBoo75+fVVpN9EPX3y26nbxuXvWsYhE/toScD5ssN3FE8YByVNfSZitWbuZPCLSoRtMZwzxfd8gVbinkp1hsUgROk=

将命名空间与类、函数和枚举合并

ORE0ZYtvg9sTKz8YhcU12N27zBn5gI3WoHkVHO6GIc/Gar4zyzWjxaJnL2lMGK+a+15OCZMjMuzKIAtUdPJoK6AtfLLjmzsDxQgIX/Et9vmyOusaTZH2e2oQ2tbHDVXCXeeV+pn17QXAAR8CEOTRuHYcqWKr6mjCvduLuvF8SQLnm1QvMDmuqnjYgeVrpgU3SCpkyxyyFMWr2CHfjuz78OT2MjQVH7WVTvPC/yBP8ez6y2CG0ue8qb65GolCluE8i06jj+gk8/rzCcqJ4gwISNQ3rpQ2FG6nhAIZpkCv+kOU7MUDnLwI05HJJmqCRt21U2srQkBRQxTFwXub8gyS1/mIW8l1/6Eg3bSmWiGHERtWdge0DuAcydilkvSzwuyG

将命名空间与类合并

a8gOI2zeK5v6LFNt1lHv4NTD/LKW4MWM1O4C2qi7XxnEiG9lloBTkpj2HOpQouDcjLwtoz5L+QpzddsMZTU/Gg==

class Album {
  label: Album.AlbumLabel;
}
namespace Album {
  export class AlbumLabel {}
}

6HYlSVNt/1ySYpT6DXpmgDpgw8e2aTdHUxsqWXT0krQu+vxErsGean9cGZcd1DcMKwk+C563BIV83PZoJRCSOddigzy+GyUCMKZL6jqRiumn3fwmRGTVsiduitjaXM6/pBG2CZxMnKqic1UBF2u9e6po7muiSFPY5fdgCUAPP8rsnUZ1p2e6tNzTHQ/s7xRxiG2hBIjEv0aRFOjdEYp3KJedNNkzreIwBDCCyAbz17O9X2h/dJUGC4/oSISQWG0uJ90tEi6wm6+efyQD0lOZctPbSPx4/zDaqwviPb/DvAC33AqVZ5++QLk8kTLcj6jyrxbomLdqqZz+L3q0BrSdor/6UxzxfttDiu4HovlUom56Hl2YEGo0aW/d7BMCTx3oAB5BTtVTMoN/VFI7ikQb2T7fP1D9WdPKTXfZ0VAebz545NItT/4mYIISRzBN7saV

qB8euQTV8Cip9vBD1uMd88+/iLs+LxRWO5+0SwajQWFng3YUUuGV1bpk0VdAplTTySCVeFhn8hVVedvaqZAsZQqmm0IGTw5mHZLNaBagWGDpcHtu2l2yq0/DTyP1ceywxFTHEm/Xy/ZOT3AiLICZcJ4hZghwQ58Oo5d32Me7iz2psA87xxZVB1noHxlp0SZDJmie9gOTxn3oRy1PD7GGjzvm/tDFJulWnmOSnpHPBQdBM2vTv5q23LmTvy6fB4lbt5N8K/AQlNTLMeX9AJhKMIKFHem7eJGJyiTWfxQpqI4=

function buildLabel(name: string): string {
  return buildLabel.prefix + name + buildLabel.suffix;
}

namespace buildLabel {
  export let suffix = "";
  export let prefix = "Hello, ";
}

console.log(buildLabel("Sam Smith"));

ydOoImOq1H6VmUy8l5hcwPti/0g/9CYl0re2hp8h45eJ1SlrLVYvT6ZnDtrBRv5io5o/4KpGXXbK9idThyejdHEDJJ74+5EAH79EByelNco=

enum Color {
  red = 1,
  green = 2,
  blue = 4,
}

namespace Color {
  export function mixColor(colorName: string) {
    if (colorName == "yellow") {
      return Color.red + Color.green;
    } else if (colorName == "white") {
      return Color.red + Color.green + Color.blue;
    } else if (colorName == "magenta") {
      return Color.red + Color.blue;
    } else if (colorName == "cyan") {
      return Color.green + Color.blue;
    }
  }
}

不允许的合并

rglk8gM+HnaF4aahD12eudlGrZab3amS1wAR/5s+deIapWmJ2g/Z+3WnwbxgpSuBkJrVzmsdNqelYLKfB6BhmDVSK2w2IDg9rrJMgPMQWNZU3hgrsllud/c8Bq1P4V5VVOl7NQrFN+qljrPTjJ1/m1Yc7AefiiAX5h7b6qPyMsQeMXpd+CZR/jqsaWwJR6hdr7iZilS4ONvA4NRdy06Ol9okmT9nWfBhRDejGZFp5BeILjv+Zx7I903H0wvD6BgHrpEzaK3H5VwarOcLEmol52xyclowoCOWVKSkeU00//EiBhGKzZGOM0KXjPC11aGw

模块扩充

Ck8mrHA2F+w5p/SqELrjZzgDAA6o9xgiTIFhp2dg3g/1fQq+4VvVV+Se/S+VIpiXP9ywHkwooFxa0lwhjRA7ll6vUCbkq2NwHEmyS/su69Pz5ij4BwA5WOv004juP/EagDt+aMs/doEWnJLtQUsZOcS8gr4MJ2cGgpwBJ3AzvScvCWfh2d0BtzXIKTXWKOl7zgRlR9x3BK9H69RSiXaYCg==

// observable.ts
export class Observable<T> {
  // ... implementation left as an exercise for the reader ...
}

// map.ts
import { Observable } from "./observable";
Observable.prototype.map = function (f) {
  // ... another exercise for the reader
};

B+NAD51XhybNq3UNgtOIQNDjYgV8Tn5bF1Ft/JgfIWj3FO9urmaxL96Q9mcd0DX3MVGR4bFT1yKFiq03Cx1mgTzlmPjod4SZ4k4CwjiUjX1iNXhVmfKTCHNjWq7hf79dWttLNiB/P8g/82mXxVsxq29hlJNbOjFfBvdk+VftLQpzUi0OWTdMfsSTqkdAjNg99cfk3hokYb7gz3UdDBtX3w==

// observable.ts
export class Observable<T> {
  // ... implementation left as an exercise for the reader ...
}

// map.ts
import { Observable } from "./observable";
declare module "./observable" {
  interface Observable<T> {
    map<U>(f: (x: T) => U): Observable<U>;
  }
}
Observable.prototype.map = function (f) {
  // ... another exercise for the reader
};

// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());

46JfrsX6rN+te/DwnV1Zw/fmFWeDQUOGfOFMjSBrT56h6WUf1oohdvpWbdiLRmXdpYd0L3aDi/+ldttzBNcTBn+Tsz2hJPCf873XxJ8B9r+OdteQFJMr3dnFSSFY7vYpZmyuqx++rU3STxZCCTYfX3mg21aF4n0dGdo9IAtRhSQIFj6k59CIpc0jzlnio9/aGfq2Yxpzb58bQ7P6gUjmS3YqkBa7KEfKu7WFMYE8MW51X/2bd6ppOeVXHft2bAxO0A+xkiHOF5PWdeheqmqLOPWEpftxqOsT755NdKDX1JJkxrtRnpx5MCwRPN5ILJPrYHSVQn8T6ynGwtdxneZj4RttWcds34TEwnt8wHU7l9N+ZI/k7D7JbQ53NVs/8RRh

wcuipdKYHGmpDDdX58naWN2eOiwiHM3OfQGgri0ImM1whSMOiKXxq2OoXvgZ9iKu

  1. 您不能在扩充中声明新的顶级声明——只是对现有声明的补丁。
  2. 默认导出也不能扩展,只能命名导出(因为您需要通过导出名称扩展导出,并且 default 是保留字 - 详情请参阅 #14080

全局增强

Hntxz8Hku8A48Oaaq4wMGvRxWA2WAQ2Dq4vGxmEqzCjM7/RKh/Ba+F1S2lENLqZuYR7GGS5dXOLRa3WizG7W7A==

// observable.ts
export class Observable<T> {
  // ... still no implementation ...
}

declare global {
  interface Array<T> {
    toObservable(): Observable<T>;
  }
}

Array.prototype.toObservable = function () {
  // ...
};

XRVLSW7gMoPbVTBQrwch24r5HFZjI3V7PMIHLZ+D+h7n5ecfXYo6ZJlZHD3Fq6DTP9JYTotIdODWxZRdOaOTFg==