Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typo in declaration merging #207

Merged

Conversation

Unuuuuu
Copy link
Contributor

@Unuuuuu Unuuuuu commented Jan 19, 2023

non-function member가 함수 멤버로 잘못 번역되어 있어 비-함수 멤버로 수정이 필요합니다.

In English:
Non-function members of the interfaces should be unique. If they are not unique, they must be of the same type. The compiler will issue an error if the interfaces both declare a non-function member of the same name, but of different types.

In Korean:
인터페이스의 비-함수 멤버는 고유해야 합니다. 만약 고유하지 않으면, 모두 같은 타입이어야 합니다. 인터페이스가 동일한 이름의 함수 멤버 -> 비-함수 멤버를 선언하지만 다른 타입으로 선언하는 경우 컴파일러는 error를 발생시킵니다.

@github-actions
Copy link
Contributor

Thanks for the PR!

This section of the codebase is owned by @bumkeyy, @yeonjuan, @guyeol, and @dvlprsh - if they write a comment saying "LGTM" then it will be merged.

@github-actions
Copy link
Contributor

Translation of Declaration Merging.md

title: Declaration Merging
layout: docs
permalink: /ko/docs/handbook/declaration-merging.html
oneline: How merging namespaces and interfaces works

translatable: true

Introduction

Some of the unique concepts in TypeScript describe the shape of JavaScript objects at the type level.
A unique example of TypeScript is the concept of "merging declarations."
Understanding this concept will have many benefits when working with traditional JavaScript.
It will also open the door to advanced abstraction concepts.

Back to the point, "merging declarations" means that the compiler merges two separate declarations declared with the same name into one definition.
This merged definition has the characteristics of both original declarations. You can merge any number of declarations to merge;
Do not limit yourself to combining only two declarations.

Basic Concepts

In TypeScript, a declaration creates an entity of at least one of three groups: namespace, type, or value.
The namespace-creation declaration uses dot notation to create a namespace with a name to access.
A type-generated declaration creates a type that is bound to a given name and represented in the declared form.
Finally, the value-generation declaration produces output that can be viewed by JavaScript.

Declaration Type Namespace type value
Namespace X X
class X X
Enumeration X X
interface X
Type alias X
function X
variable X

Understanding the results produced by each declaration helps you understand the results of the merges when you merge declarations.

Merging Interfaces

The simplest and most common type of declaration merging is interface merging.
At the most basic level, merging mechanically combines the members of two declarations into a single interface of the same name.

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

interface Box {
  scale: number;
}

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

The non-functional members of an interface must be unique.
If they are not unique, they must all be of the same type.
If an interface declares a non-functional member with the same name, but with a different type, the compiler raises an error.

For function members, each function member with the same name is treated as overloading the same function.
It is also important to note that if you merge interface A with later interface A, the second interface will have a higher priority than the first interface.

For example:

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

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

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

The above three interfaces can be merged into a single declaration as follows:

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

Note that the elements in each group maintain the same order, but the group itself is placed first as it becomes overloaded later.

There are exceptions to this rule called specialized signatures.
What if unity If there is a parameter of string literal type (for example, if the string literal is not union), the signature is promoted to the top of the merged overload list.

For example, the following interfaces are merged:

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;
}

DocumentThe result of the merged declaration of is as follows:

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

Merging Namespaces

Like interfaces, namespaces with the same name merge with namespace members.
Because namespaces create both namespaces and values, it's important to understand how the two merge.

To merge namespaces, type definitions are merged from the exported interfaces declared in each namespace, forming a single namespace with merged interface definitions inside.

To merge namespace values, if each declaration location already has a namespace with the specified name, the namespace value is expanded by adding the exported member of the second namespace to the first existing namespace.

An example of this: Animals Merge declarations of:

namespace Animals {
  export class Zebra {}
}

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

They are as follows:

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

  export class Zebra {}
  export class Dog {}
}

This model of merging namespaces is a good starting point, but we need to understand what happens to non-export members.
Members that are not exported are visible only in the original namespace (the namespace that has not been merged). This means that after the merge, members merged with other declarations will not see members that are not exported.

This can be seen more clearly in the example below:

namespace Animal {
  let haveMuscles = true;

  export function animalsHaveMuscles() {
    return haveMuscles;
  }
}

namespace Animal {
  export function doAnimalsHaveMuscles() {
    return haveMuscles; // 오류, haveMuscles가 여기에 접근할 수 없기 때문에
  }
}

haveMuscles is not exported, sharing namespaces that are not identically merged. animalsHaveMuscles Only functions can see this symbol.
doAnimalsHaveMuscles functions, merged Animal Even if you are a member of the namespace, you cannot see members that are not exported.

Merging namespaces with classes, functions, and enums

Namespaces are flexible enough to merge with other types of declarations.
To do this, the namespace declarations must conform to the declarations to be merged. The resulting declaration has properties of both declaration types.
This allows TypeScript to model patterns in JavaScript and other programming languages.

Merging Namespaces with Classes

This part describes how the inner class is described.

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

The visibility rules for merged members are Merging Namespaces As described in the session, AlbumLabelYou must export the class before you can see the merged class.
The end result is a managed class within another class.
You can also use namespaces to add more static members to existing classes.

In addition to the inner class pattern, you should also be familiar with extending functions by creating functions and adding properties in JavaScript.
TypeScript can securely preserve and define types through declaration merging.

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"));

Similarly, namespaces can extend the enumeration of static members:

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;
    }
  }
}

Disallowed Merges

Not all merges are allowed in TypeScript.
A class cannot be merged with another class or variable.
For information about replacing class merging, see Mixins in TypeScript section.

Module Augmentation

JavaScript does not support merging modules, but you can patch existing objects by importing and updating them.
Let's look at an easy observable example:

// observable.ts
export class Observable<T> {
  // ... 연습을 위해 남겨둠 ...
}

// map.ts
import { Observable } from "./observable";
Observable.prototype.map = function (f) {
  // ... 연습을 위해 남겨둠
};

This works fine with TypeScript, but the compiler Observable.prototype.mapI don't know about it.
Module augmentation can inform the compiler:

// observable.ts
export class Observable<T> {
  // ... 연습을 위해 남겨둠 ...
}

// 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) {
  // ... 연습을 위해 남겨둠
};

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

The module name is import/exportis interpreted in the same way as the module specifier of .
For more information, see moduleSee .
The enriched declarations are then merged as if they were declared in the same file as the original.

However, keep in mind two limitations:

  1. You cannot make a new top-level declaration for enrichment -- you can only patch an existing declaration.
  2. Default exports cannot be augmented, only exports with names (they must be expanded with that name; defaultis a reserved word - for more information, see #14080)

Global augmentation

You can add declarations to the global scope from inside the module:

// observable.ts
export class Observable<T> {
  // ... 연습을 위해 남겨둠 ...
}

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

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

Global enrichment has the same behavior and limitations as module augmentation.

Generated by 🚫 dangerJS against 10322cb

@bumkeyy
Copy link
Contributor

bumkeyy commented Feb 11, 2023

@Unuuuuu

수정 감사합니다 👍

@bumkeyy
Copy link
Contributor

bumkeyy commented Feb 11, 2023

LGTM

@github-actions github-actions bot merged commit 7415553 into microsoft:main Feb 11, 2023
@github-actions
Copy link
Contributor

Merging because @bumkeyy is a code-owner of all the changes - thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants