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 minor typo in ko/ #217

Merged
merged 2 commits into from
Jun 6, 2023
Merged

Conversation

yusunghyun
Copy link
Contributor

Fix Typo

  • lenght -> length
  • scirpt -> script

@github-actions
Copy link
Contributor

github-actions bot commented Jun 6, 2023

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

github-actions bot commented Jun 6, 2023

Translation of Modules.md

title: Modules
layout: docs
permalink: /ko/docs/handbook/modules.html
oneline: How modules work in TypeScript

translatable: true

Starting with ECMAScript 2015, JavaScript has a module concept. TypeScript shares this concept.

The module runs within its own scope, not in a global scope; In other words, variables, functions, classes, etc. declared within the module are export form It is not visible outside the module unless explicitly exported using one of them.
Conversely, in order to use the exported variables, functions, classes, interfaces, etc. from other modules, import form You must import using one of them.

The module is declarative; The relationships between modules are specified from the file-level imports and exports perspectives.

The module uses the module loader to import other modules.
At runtime, the module loader must find and execute all dependencies of the module before running the module.
Famous module loaders used in JavaScript include: CommonJS Node.js loader for modules and web applications AMD For modules RequireJS There is a loader.

Like ECMAScript 2015, TypeScript uses top-level import or exportis considered a module.
Conversely, the top-level import or export Files that do not have a declaration are treated as scripts that can be used in the global scope (as well as in modules).

Export

Exporting a declaration

export You can export all declarations (variables, functions, classes, type aliases, interfaces) by adding keywords.

StringValidator.ts
export interface StringValidator {
    isAcceptable(s: string): boolean;
}
ZipCodeValidator.ts
import { StringValidator } from "./StringValidator";

export const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}

Export statements

The Export statement comes in handy when you need to rename something to export for the user. The above example could be written as follows:

class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };

Re-export (Re-exports)

Often, a module extends other modules and partially exposes some of its functionality.
Re-export does not import regionally, nor does it introduce local variables.

ParseIntBasedZipCodeValidator.ts
export class ParseIntBasedZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && parseInt(s).toString() === s;
    }
}

// 기존 validator의 이름을 변경 후 export
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";

Optionally, one module can wrap one or multiple modules, export * from "module" You can combine everything you do using the export syntax.

AllValidators.ts
export * from "./StringValidator"; // 'StringValidator' 인터페이스를 내보냄
export * from "./ZipCodeValidator";  // 'ZipCodeValidator' 와 const 'numberRegexp' 클래스를 내보냄
export * from "./ParseIntBasedZipCodeValidator"; // 'ParseIntBasedZipCodeValidator' 클래스를 내보냄
                                                 // 'ZipCodeValidator.ts' 모듈 에 있는
                                                 // 'ZipCodeValidator' 클래스를
                                                 // 'RegExpBasedZipCodeValidator' 라는 별칭으로 다시 내보냄

Import

Import is as easy as exporting from a module.
The export declaration is below import Use one of the forms to import:

Import a single export from a module

import { ZipCodeValidator } from "./ZipCodeValidator";

let myValidator = new ZipCodeValidator();

You can import it by modifying the name.

import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();

Import the entire module into a single variable, and use it to access the module exports

import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();

Import a module for side-effects only

Although not recommended, some modules are set to some global state so that they can be used by others.
These modules either don't have any exports, or the user is not interested in them.
To import these modules, use the following:

import "./my-module.js"

Importing Types

Prior to TypeScript 3.8, importI was able to import the type using .
In TypeScript 3.8, import Door, or import typeYou can import types using the

// 동일한 import를 재사용하기
import {APIResponseType} from "./api";

// 명시적으로 import type을 사용하기
import type {APIResponseType} from "./api";

import typeis always removed from JavaScript, and tools like Babel isolatedModules Compiler flags allow you to make better assumptions about your code.
3.8 Release NotesYou can read more information at

Default exports

Each module is optionally default You can export export.
The default export is default It is represented by keywords; One per module default Export only.
default export uses a different import form to import.

default Exports are really convenient.
For example, a library like jQuery would use jQuery or $You can have a default export such as, $I jQueryYou can import it with a name such as

JQuery.d.ts
declare let $: JQuery;
export default $;
App.ts
import $ from "jquery";

$("button.continue").html( "Next Step..." );

Class and function declarations can be written directly with default exports.
The default export class and function declaration names are optional.

ZipCodeValidator.ts
export default class ZipCodeValidator {
    static numberRegexp = /^[0-9]+$/;
    isAcceptable(s: string) {
        return s.length === 5 && ZipCodeValidator.numberRegexp.test(s);
    }
}
Test.ts
import validator from "./ZipCodeValidator";

let myValidator = new validator();

or

StaticZipCodeValidator.ts
const numberRegexp = /^[0-9]+$/;

export default function (s: string) {
    return s.length === 5 && numberRegexp.test(s);
}
Test.ts
import validate from "./StaticZipCodeValidator";

let strings = ["Hello", "98052", "101"];

// validate 함수 사용하기
strings.forEach(s => {
  console.log(`"${s}" ${validate(s) ? "matches" : "does not match"}`);
});

default Exports can also be valued:

OneTwoThree.ts
export default "123";
Log.ts
import num from "./OneTwoThree";

console.log(num); // "123"

Export all as x

In TypeScript 3.8, when the following name is re-exported to another module, it is like a shortcut: export * as nsYou can use:

export * as utilities from "./utilities";

If you take all the dependencies from the module and make them into exported fields, you can import them as follows:

import { utilities } from "./index";

export =and import = require() (export = and import = require())

Both CommonJS and AMD typically include all exports of the module. exports It has the concept of an object.

In addition exports It also supports replacing objects with single, user-defined objects.
Default exports act as a fallback to this behavior; However, the two are not compatible.
TypeScript is used to model existing CommonJS and AMD workflows. export =Support.

export = The syntax specifies a single object that is exported from the module.
It can be a class, interface, namespace, function, or enumeration.

export = When exporting a module using TypeScript-specific import module = require("module")to import the module.

ZipCodeValidator.ts
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
export = ZipCodeValidator;
Test.ts
import zip = require("./ZipCodeValidator");

// 시험용 샘플
let strings = ["Hello", "98052", "101"];

// 사용할 Validators
let validator = new zip();

// 각 문자열이 각 validator를 통과했는지 보여줍니다
strings.forEach(s => {
  console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);
});

Code Generation for Modules

During compilation, depending on the specified module target, the compiler sends Node.js (CommonJS), require.js (AMD), UMD, SystemJSor ECMAScript 2015 native modules (ES6) Module-Generate code suitable for loading systems.
In the generated code, define, require and register For more information about the calling function, check the documentation for each module loader.

This simple example shows how the names used during import and export are converted to module loading code.

SimpleModule.ts
import m = require("mod");
export let t = m.something + 1;
AMD / RequireJS SimpleModule.js
define(["require", "exports", "./mod"], function (require, exports, mod_1) {
    exports.t = mod_1.something + 1;
});
CommonJS / Node SimpleModule.js
var mod_1 = require("./mod");
exports.t = mod_1.something + 1;
UMD SimpleModule.js
(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports); if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./mod"], factory);
    }
})(function (require, exports) {
    var mod_1 = require("./mod");
    exports.t = mod_1.something + 1;
});
System SimpleModule.js
System.register(["./mod"], function(exports_1) {
    var mod_1;
    var t;
    return {
        setters:[
            function (mod_1_1) {
                mod_1 = mod_1_1;
            }],
        execute: function() {
            exports_1("t", t = mod_1.something + 1);
        }
    }
});
Native ECMAScript 2015 modules SimpleModule.js
import { something } from "./mod";
export var t = something + 1;

Simple Example

Below, we incorporate the Validator implementation used in the previous example to export with a single name in each module.

To compile, you must specify the module target on the command line. For Node.js: --module commonjsuse;
For require.js --module amdUse . Like what:

tsc --module commonjs Test.ts

Once compiled, each module has a separate .jsfile.
As with reference tags, the compiler uses the importFollow the statement to compile the dependent files.

Validation.ts
export interface StringValidator {
    isAcceptable(s: string): boolean;
}
LettersOnlyValidator.ts
import { StringValidator } from "./Validation";

const lettersRegexp = /^[A-Za-z]+$/;

export class LettersOnlyValidator implements StringValidator {
    isAcceptable(s: string) {
        return lettersRegexp.test(s);
    }
}
ZipCodeValidator.ts
import { StringValidator } from "./Validation";

const numberRegexp = /^[0-9]+$/;

export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
        return s.length === 5 && numberRegexp.test(s);
    }
}
Test.ts
import { StringValidator } from "./Validation";
import { ZipCodeValidator } from "./ZipCodeValidator";
import { LettersOnlyValidator } from "./LettersOnlyValidator";

// 시험용 샘플
let strings = ["Hello", "98052", "101"];

// 사용할 validator
let validators: { [s: string]: StringValidator; } = {};
validators["ZIP code"] = new ZipCodeValidator();
validators["Letters only"] = new LettersOnlyValidator();

// 각 문자열이 validator를 통과하는지 보여줌
strings.forEach(s => {
    for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
    }
});

Optional Module Loading and Other Advanced Loading Scenarios

Depending on the situation, you can make the module load only under certain conditions.
In TypeScript, you can use the patterns below to implement this and other advanced loading scenarios to invoke the module loader directly without losing the stability of the type.

The compiler detects the use of each module within the exposed JavaScript.
If a module identifier is used only as a type indication and not as an expression, the require The call does not occur.
Removing unused references can optimize performance, and you can selectively load those modules.

The core idea of this pattern is import id = require("...") It means that it is possible to access the type exposed as a module through the door.
below if As you can see in the block, the module loader is (requirevia) is called dynamically.
This feature leverages reference-remove optimization, so modules can be loaded only when needed.
For that pattern to work, importIt is important to note that symbols defined by are used only in type positions (i.e. not in JavaScript emit positions).

In order to maintain type safety, typeof You can use keywords.
typeof When a keyword is used in the type position, it creates the type of the value, in this case the type of the module.

Dynamic Module Loading in Node.js .js
declare function require(moduleName: string): any;

import { ZipCodeValidator as Zip } from "./ZipCodeValidator";

if (needZipValidation) {
    let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator");
    let validator = new ZipCodeValidator();
    if (validator.isAcceptable("...")) { /* ... */ }
}
Sample: Dynamic Module Loading in require.js .js
declare function require(moduleNames: string[], onLoad: (...args: any[]) => void): void;

import * as Zip from "./ZipCodeValidator";

if (needZipValidation) {
    require(["./ZipCodeValidator"], (ZipCodeValidator: typeof Zip) => {
        let validator = new ZipCodeValidator.ZipCodeValidator();
        if (validator.isAcceptable("...")) { /* ... */ }
    });
}
Sample.js: Dynamic Module Loading in System.js
declare const System: any;

import { ZipCodeValidator as Zip } from "./ZipCodeValidator";

if (needZipValidation) {
    System.import("./ZipCodeValidator").then((ZipCodeValidator: typeof Zip) => {
        var x = new ZipCodeValidator();
        if (x.isAcceptable("...")) { /* ... */ }
    });
}

Working with Other JavaScript Libraries

To describe what a library looks like that isn't written in TypeScript, you need to declare the API that exposes the library.

We call declarations that do not define an implementation "ambient".
These declarations are generally .d.ts It is defined in the file.
If you are familiar with C/C++, .h You can think of it as a file.
Let's look at some examples.

Ambient Modules

In Node.js most tasks are performed by loading one or more modules.
As a top-level export declaration, each module is declared .d.ts file, but one larger .d.ts It is more convenient to create modules from files.
To do this, we use a structure similar to the ambient namespace, but with the quoted module name and the module Use keywords.
Like what:

node.d.ts (brief excerpt)
declare module "url" {
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }

    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}

declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export var sep: string;
}

now /// <reference> node.d.tsand then perform the import url = require("url"); or import * as URL from "url"to load the module.

/// <reference path="node.d.ts"/>
import * as URL from "url";
let myUrl = URL.parse("http://www.typescriptlang.org");

Shorthand ambient modules

If you don't write a declaration before using a new module, you can use a shorthand declaration to get started quickly.

declarations.d.ts
declare module "hot-new-module";

All imports from the shorthand module are any It has a type.

import x, {y} from "hot-new-module";
x(y);

Wildcard module declarations

SystemJSI
AMDModule loaders such as can import non-JavaScript content.
Both typically use prefixes or suffixes to indicate special loading meanings.
To deal with these cases, you can use wildcard module declarations.

declare module "*!text" {
    const content: string;
    export default content;
}
// 일부는 다른 방법으로 사용합니다.
declare module "json!*" {
    const value: any;
    export default value;
}

now "*!text" I "json!*"You can import the ones that match the

import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

UMD modules

Some libraries are used by many module loaders, or are designed to be used without module loading (global variables).
This can be called UMD It's called a module.
These libraries can be accessed through import or global variables.
Like what:

math-lib.d.ts
export function isPrime(x: number): boolean;
export as namespace mathLib;

Libraries can be used as imports within modules:

import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 오류: 모듈 내부에서 전역 정의를 사용할 수 없습니다.

It can also be used as a global variable, but only within a script.
(The script is a file that does not have imports or exports.)

mathLib.isPrime(2);

Guidance for structuring modules

Export as close to top-level as possible

There should be as little friction as possible when the user of the module uses the export module.
Adding too many levels of overlap tends to make them unwieldy, so you need to think carefully about how you structure them.

Exporting namespaces in a module is an example of adding too many nested layers.
Namespaces sometimes have their uses, but when you use the module, you add an additional level of indirection.
This can quickly become a painful point for the user, and is usually unnecessary.

I have a similar problem with the static method of the class that I exported - it adds a nested layer to the class itself.
Consider simply exporting helper functions unless you want to elevate the wording or intent in a clearly useful way.

unity classI functionIf you want to export, export default(If you're only exporting a single class or function, use export default)

Just as "top-level export" reduces friction for module users, so does the introduction of default export.
If the main purpose of the module is to store one specific export, consider exporting as the default export.
This makes it easier to import and actually use import.
Like what:

MyClass.ts

export default class SomeType {
  constructor() { ... }
}

MyFunc.ts

export default function getThing() { return "thing"; }

Consumer.ts

import t from "./MyClass";
import f from "./MyFunc";
let x = new t();
console.log(f());

This is optimal for users. The name you want for the type (in this case, the name t) and doesn't need to over-dot to find the object.

If you're exporting multiple objects, put them all at top-level

MyThings.ts

export class SomeType { /* ... */ }
export function someFunc() { /* ... */ }

When importing on the contrary:

Explicitly list imported names

Consumer.ts

import { SomeType, someFunc } from "./MyThings";
let x = new SomeType();
let y = someFunc();

Use the namespace import pattern if you're importing a large number of things

MyLargeModule.ts

export class Dog { ... }
export class Cat { ... }
export class Tree { ... }
export class Flower { ... }

Consumer.ts

import * as myLargeModule from "./MyLargeModule.ts";
let x = new myLargeModule.Dog();

Re-export to extend

Often it is necessary to expand the functionality of the module.
The general JS pattern is similar to how the JQuery extension works. _extensions_to augment an existing object.
As mentioned earlier, modules are like global namespace objects, such as merge Don't.
The recommended method here is to change the existing object to the Without deformation The new functionality is to export the object.

Calculator.ts Consider a simple calculator implementation defined in a module.
This module also passes a list of input strings and creates a helper function that allows you to test the functionality of the calculator by writing the export results.

Calculator.ts

export class Calculator {
    private current = 0;
    private memory = 0;
    private operator: string;

    protected processDigit(digit: string, currentValue: number) {
        if (digit >= "0" && digit <= "9") {
            return currentValue * 10 + (digit.charCodeAt(0) - "0".charCodeAt(0));
        }
    }

    protected processOperator(operator: string) {
        if (["+", "-", "*", "/"].indexOf(operator) >= 0) {
            return operator;
        }
    }

    protected evaluateOperator(operator: string, left: number, right: number): number {
        switch (this.operator) {
            case "+": return left + right;
            case "-": return left - right;
            case "*": return left * right;
            case "/": return left / right;
        }
    }

    private evaluate() {
        if (this.operator) {
            this.memory = this.evaluateOperator(this.operator, this.memory, this.current);
        }
        else {
            this.memory = this.current;
        }
        this.current = 0;
    }

    public handleChar(char: string) {
        if (char === "=") {
            this.evaluate();
            return;
        }
        else {
            let value = this.processDigit(char, this.current);
            if (value !== undefined) {
                this.current = value;
                return;
            }
            else {
                let value = this.processOperator(char);
                if (value !== undefined) {
                    this.evaluate();
                    this.operator = value;
                    return;
                }
            }
        }
        throw new Error(`Unsupported input: '${char}'`);
    }

    public getResult() {
        return this.memory;
    }
}

export function test(c: Calculator, input: string) {
    for (let i = 0; i < input.length; i++) {
        c.handleChar(input[i]);
    }

    console.log(`result of '${input}' is '${c.getResult()}'`);
}

Exposed test This is a simple calculator test that uses a function.

TestCalculator.ts

import { Calculator, test } from "./Calculator";


let c = new Calculator();
test(c, "1+2*33/11="); // 9 출력

Inherit this so that you can enter a number other than 10. ProgrammerCalculator.tsLet's make it.

ProgrammerCalculator.ts

import { Calculator } from "./Calculator";

class ProgrammerCalculator extends Calculator {
    static digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];

    constructor(public base: number) {
        super();
        const maxBase = ProgrammerCalculator.digits.length;
        if (base <= 0 || base > maxBase) {
            throw new Error(`base has to be within 0 to ${maxBase} inclusive.`);
        }
    }

    protected processDigit(digit: string, currentValue: number) {
        if (ProgrammerCalculator.digits.indexOf(digit) >= 0) {
            return currentValue * this.base + ProgrammerCalculator.digits.indexOf(digit);
        }
    }
}

// 새로 상속된 calculator를 Calculator로 export 하기
export { ProgrammerCalculator as Calculator };

// 또한 헬퍼 함수도 export 하기
export { test } from "./Calculator";

New ProgrammerCalculator The module is Calculator It exports an API similar to a module, but does not augment the objects of the original module.
Here is the test for the ProgrammerCalculator class:

TestProgrammerCalculator.ts

import { Calculator, test } from "./ProgrammerCalculator";

let c = new Calculator(2);
test(c, "001+010="); // 3 출력

Do not use namespaces in modules

When you first apply a module-based configuration, you typically tend to wrap exports in an additional namespace layer.
The module has its own scope, and only exported declarations are visible outside the module.
With this in mind, namespaces rarely change values when dealing with modules.

On the front of the configuration, namespaces are convenient for grouping logically related objects and types into a global scope.
For example, in C#, you can find all collection types in System.Collections.
By organizing types into hierarchical namespaces, users of those types have a good experience of "discovery".
On the other hand, a module must already exist in the file system.
To interpret paths and file names, you can use a logical construction scheme.
You can use the /collections/generic/ folder with the list module.

Namespaces are important to avoid naming conflicts in the global scope.
For example My.Application.Customer.AddFormand My.Application.Order.AddForm -- Both types have the same name, but have different namespaces.
However, this is not a problem in the module.
There is no reason why two objects within a module should have the same name.
In terms of usage, the user of a particular module chooses the name to use to refer to the module, so accidental name conflicts are not possible.

For more information about modules and namespaces, see Namespaces and ModulesPlease refer to

Red Flags

The following are all red flags for module structuring: If any of the following applies to the file, double-check that you haven't attempted to create a namespace for the external module:

  • Only top-level declarations export namespace Foo { ... }In file (Fooand move everything to the 'higher' level)
  • Same as top-level position export namespace Foo {Multiple files with a single FooDon't think it's going to be combined!)
Translation of Project References.md

title: Project References
layout: docs
permalink: /ko/docs/handbook/project-references.html
oneline: How to split up a large TypeScript project

translatable: true

Project Reference is a new feature in TypeScript 3.0 that allows you to organize TypeScript programs into smaller pieces.

This greatly improves build times and enforces logical separation between components, allowing you to organize your code in a new and better way.

It also works with project references for quick TypeScript builds. tsc The new mode of --build Flags have been introduced.

An Example Project

Let's take a look at some pretty common programs and see how a project reference can help you better organize them.
converterand unitsLet's imagine that there are two modules in a project, and each module has a corresponding test file:

/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json

The test file is imported into the implementation file and the test is carried out:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

Previously, this structure behaved awkwardly if you used a single tsconfig file:

  • It is possible to import test files from the implementation file
  • I probably didn't want to. srcdoes not appear in the output folder name. testand srcIt is not possible to build them at the same time
  • Inside the implementation file Contents It will never raise a new error, but it will never cause a new error for the test file. _Type Inspection_You will need to redo it
  • Changing only the test file does not change anything, but you need to re-check the type of implementation file

Using multiple tsconfig files to solve one of these problems _Some_This can be solved, but a new problem arises:

  • Since there is no built-in up-to-date inspection, it is always tscmust be run twice
  • tscCalling twice incurs more startup time overhead
  • tsc-wcannot run multiple config files at once

A project reference can solve all of these problems.

What are the project references? (What is a Project Reference?)

tsconfig.json The file has a new top-level property referenceIt has. This is an array of objects that specify the project to be referenced:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Each reference's path The property is tsconfig.json Point to the directory where the file is located, or to the config file itself (which can have any name).

When you refer to a project, something new happens:

  • If you import a module from a referenced project, you can use the module's output Load the declaration file instead (.d.ts)
  • If the referenced project is outFileWhen you create an output file .d.ts The declaration of the file is exposed within this project
  • The build mode (shown below) automatically builds the referenced project if necessary

Separating into multiple projects greatly improves type checking and compilation speeds, reduces memory usage when using the editor, and improves logical grouping of programs.

composite

The referenced project must be a new composite The setting must be enabled.
This setting is necessary so that TypeScript can quickly determine where to find the output of the referenced project.
composite When you enable the flag, a few things change:

  • What if rootDir If the setting is not explicitly specified, the default value is tsconfig The directory where the file is located
  • All implementation files must be include Fits the pattern or files It must be inside an array. If this constraint is violated, tsctells you which files are not specified
  • declarationmust be turned on

declarationMaps

Declaration Source MapWe've also added support for
What if --declarationMap, you can use editor features such as "Go to Definition" and rename to transparently navigate in supported editors and modify code across project boundaries.

prependand outFile (prepend with outFile)

In the reference prepend You can enable the appending of dependencies using the options:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

Adding a project includes the output of the project on top of the output of the current project.
This is because .js Files .d.ts All of them work in the file, and the source map file is also released correctly.

tscuses only existing files on disk for this task, so that the output of a project may appear more than once in the resulting file, making it possible to create a project in which the correct output file cannot be generated.
For example:

   A
  ^ ^
 /   \
B     C
 ^   ^
  \ /
   D

In this situation, it is important not to add to each reference, because DOn the output of the AThis is because two copies of the - which can lead to unforeseen consequences.

Caveats for Project References

There are a few trade-offs that you should be aware of when it comes to project references.

Because a dependency project is built from a dependency .d.ts Because it uses files, you can inspect certain build output before you can browse through the project without seeing incorrect errors in the editor. or After cloning, you must build the project.
We are working on a .d.ts creation process that can improve this, but for now, we recommend that developers build after cloning.

Additionally, to maintain compatibility with existing build workflows, tscThe --build Unless you call the switch, it does not automatically build dependencies Is.
--buildLet's learn about.

Build Mode for TypeScript

The long-awaited feature is a smart incremental build for TypeScript projects.
In 3.0 tscIn --build Flags are now available.
This behaves more like a build manager than a simple compiler. tscIt is a new entry point for .

tsc --build (The abbreviated form is tsc -b) to do the following:

  • Locate all referenced projects
  • Detect if it's up-to-date
  • Build projects that are not up-to-date in the correct order

tsc -bYou can provide multiple config file paths to (for example. tsc -b src test).
tsc -pLike, if the config file name is tsconfig.jsonIf so, you don't need to specify a name.

tsc -b The command-line (tsc -b Commandline)

You can specify as many config files as you want:

 > tsc -b                            # 현재 디렉터리에 있는 tsconfig.json 사용
 > tsc -b src                        # src/tsconfig.json 사용
 > tsc -b foo/prd.tsconfig.json bar  # foo/prd.tsconfig.json 와 bar/tsconfig.json 사용

Don't worry about the order of the files you pass to the command line - if you need them tscBecause the dependencies are rearranged, the dependencies are always built first.

tsc -bThere are a few more flags that can be specified:

  • --verbose: Displays a detailed log of how it is going (can be combined with other flags)
  • --dry: It doesn't actually build, but it shows how it will work
  • --clean: Removes the output of the specified project (--dryIt can be combined with)
  • --force: All projects behave as if they are not up to date
  • --watch: Surveillance mode (--verboseIt cannot be combined with any other flags except )

Precautions (Caveats)

generally tscThe noEmitOnErrorIf is not enabled, the output (.jsand .d.ts) to create a .
It's very bad to do this in an incremental build system - because if one of the dependencies is not up-to-date, it will skip building the project that is currently up-to-date in the next build. once You can only see it.
For this reason, tsc -bThe noEmitOnErrorbehaves as effectively as if it were enabled in all projects.

There is no build output (.js, .d.ts, .d.ts.map, etc.), depending on whether the source control tool preserves the timestamp between the local and remote copies, after a specific source control operation. --force You may need to run the build.

MSBuild

If you have an MSBuild project, add the following to the build mode to the proj file:

    <TypeScriptBuildMode>true</TypeScriptBuildMode>

You can activate it. This enables automatic incremental builds as well as removal.

tsconfig.json / -pNote that existing TypeScript project properties are not taken into account - all settings must be managed using a tsconfig file.

Some teams have projects where tsconfig files are managed in parallel, such as Implicit It has graph order and has set up an MSBuild-based workflow.
If the solution is like this, please use the project reference with msbuildRaise tsc -pYou can continue to use it with; They are fully interoperable.

Guidance

Overall Structure

More tsconfig.json files, to centrally control common compiler options. Inheriting configuration filesYou'll want to use it.
In this way, you can change the settings in one file without modifying multiple files.

Another good way to do this is to simply add all leaf-node projects to referencesHave a files"Solution" to set to an empty array tsconfig.json It's about having a file (otherwise the file will be compiled twice because of the solution file). Starting with 3.0, at least one referenceprice tsconfig.jsonIf you are in, empty files Note that having arrays is no longer an error.

This provides a simple entry point; For example, in the TypeScript repository, src/tsconfig.json Simply because it lists all the subprojects inside tsc -b src Run to build all the endpoints.

These patterns can be seen in the TypeScript repository - as the main example src/tsconfig_base.json, src/tsconfig.jsonand src/tsc/tsconfig.jsonTake a look.

Structuring for relative modules

In general, switching repositories using relative modules doesn't require much else.
Simply use the parent folder of the tsconfig.json Place the file within each subdirectory, and ensure that it matches the intended hierarchy of the program. referenceto this config file.
outDiras an explicit subfolder of the output folder, or rootDiras the common root of all project folders.

Structuring for outFiles

outFileThe layout of compilation with is more flexible because relative paths don't matter much.
One thing to remember is that until the "last" project, prepend- which will improve build time and reduce the number of I/Os required for a given build.
The TypeScript repository itself is a good reference here - there are several "library" projects and "endpoint" projects; The "endpoint" project is kept as small as possible and only pulls the libraries that are needed.

Translation of More on Functions.md

title: More on Functions
layout: docs
permalink: /ko/docs/handbook/2/functions.html

oneline: "Let's see how functions work in TypeScript."

Whether it's a local function, a function loaded from another module, or a method of any class, a function is a fundamental component of any application.
A function is a value. And just like any value, there are many ways to describe how functions can be called in TypeScript.
Let's see how to write types that describe functions.

Function type expressions

The simplest way to describe a function is to use the Function type expressions Is.
This type is grammatically similar to the arrow function.

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}

function printToConsole(s: string) {
  console.log(s);
}

greeter(printToConsole);

(a: string) => void The syntax is "string type aas one parameter and has no return value".
Like a function declaration, if the parameter is not typed, it is implicitly anybecomes.

If the parameter name is essential Keep in mind that. Function type (string) => voidThe "any With type stringIt means a function with a parameter named !

Of course, you can use a type alias to name a function's type.

type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
  // ...
}

Call Signature

In JavaScript, functions can not only be called, but they can also have properties.
However, the function-type expression syntax does not allow you to define properties.
If we try to describe something that is callable and has properties, then the object type must be Call Signature can be expressed using .

type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
  console.log(fn.description + " returned " + fn(6));
}

This syntax is different from function-type expressions. Between the type of the parameter and the type of the return value =>Rather than :should be used.

Configuration Signatures

The JavaScript function is newIt can also be called through operators.
TypeScript uses these things mainly to create new objects. constructor It is considered to be.
You must display the call signature in front of the new By attaching keywords, _Configuration Signatures_You can write.

type SomeObject = any;
// ---cut---
type SomeConstructor = {
  new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
  return new ctor("hello");
}

JavaScript Date Some objects, such as objects, are newIt can be called with or without .
You can combine call signatures and configuration signatures in the same type at will.

interface CallOrConstruct {
  new (s: string): Date;
  (n?: number): number;
}

Generic functions

It is not uncommon to write a function in which the input value is related to the type of the output value, or the types of the two inputs are related to each other.
Let's consider for a moment a function that returns the first element of an array.

function firstElement(arr: any[]) {
  return arr[0];
}

The function does its job, but unfortunately the return type is any Is.
It would be better if the function returned the type of the array element.

In TypeScript: Generic Syntax is used to express the correlation between two values.
We use the function signature _Type Parameters_You can do that by declaring.

function firstElement<Type>(arr: Type[]): Type | undefined {
  return arr[0];
}

Type Parameters Typeto this function, and by using it in two places where it is needed, we have created a link between the input value (array) and the output (return value) of the function.
Now, when we call this function, we get a clearer type.

declare function firstElement<Type>(arr: Type[]): Type | undefined;
// ---cut---
// s는 "string" 타입
const s = firstElement(["a", "b", "c"]);
// n은 "number" 타입
const n = firstElement([1, 2, 3]);
// u는 "undefined" 타입
const u = firstElement([]);

Inference

In this example, we use TypeNote that it is not specified.
where the type is It was deduced In other words, it is automatically selected by TypeScript.

We can also use multiple type parameters.
For example, mapThe standalone version of should look like this:

// prettier-ignore
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}

// 매개변수 'n'의 타입은 'string' 입니다.
// 'parsed'는 number[] 타입을 하고 있습니다.
const parsed = map(["1", "2", "3"], (n) => parseInt(n));

In this example, TypeScript uses the Input Type and (given as input string from the array) OutputType the return value of the function expression (number) can be inferred from the

Type Constraints

We are all We have written generic functions that operate on types.
Sometimes, we want to associate two values, but only for a subset of them.
In these cases, we use _Type Constraints_to limit the types that the type parameter can accept.

Let's write a function that returns the longer of the two values.
For this task, you can use the number of length Properties are required.
extendsUsing a clause, the type parameter is changed to that type restriction I can.

// @errors: 2345 2322
function longest<Type extends { length: number }>(a: Type, b: Type) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}

// longerArray 의 타입은 'number[]' 입니다'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString 의 타입은 'alice' | 'bob' 입니다.
const longerString = longest("alice", "bob");
// 에러! Number에는 'length' 프로퍼티가 없습니다.
const notOK = longest(10, 100);

In this example, there are a few interesting points to note.
We believe that TypeScript is longestThe return type of inference I allowed it to be.
Return type inference also works with generic functions.

We TypeRewrite { length: number }As limited to, we aand b About the parameters .length I was able to access the property.
If there were no type restrictions, these values would not have access to the property because they could be of any other type that did not have the length property.

longerArrayand longerStringThe type of was inferred based on the arguments.
It's important to remember that generics are when two or more values are associated with the same type!

In the end, as we want longest(10,100)silver numberType .length You can see that the call was rejected because you didn't have any properties.

Working with limited values

The following are common mistakes when using generic-type constraints:

// @errors: 2322
function minimumLength<Type extends { length: number }>(
  obj: Type,
  minimum: number
): Type {
  if (obj.length >= minimum) {
    return obj;
  } else {
    return { length: minimum };
  }
}

This function doesn't seem to be a problem. Typesilver { length: number }, and the function is Typeor a value that satisfies that constraint.
The problem is that this function satisfies the constraints. How Not an object, but an input How It returns an object.
If this code is valid, you can write the following code that will definitely not work.

declare function minimumLength<Type extends { length: number }>(
  obj: Type,
  minimum: number
): Type;
// ---cut---
// 'arr' gets value { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// 여기서 배열은 'slice' 메서드를 가지고 있지만
// 반환된 객체는 그렇지 않기에 에러가 발생합니다!
console.log(arr.slice(0));

Specifying Type Arguments

TypeScript usually deduces the intended type from a generic call, but this is not always the case.
For example, let's say you have written a function that combines two arrays.

function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
  return arr1.concat(arr2);
}

In general, it would be wrong to call that function with an unmatched array.

// @errors: 2322
declare function combine<Type>(arr1: Type[], arr2: Type[]): Type[];
// ---cut---
const arr = combine([1, 2, 3], ["hello"]);

If you intend to do this, you can do it manually Typemust be stated.

declare function combine<Type>(arr1: Type[], arr2: Type[]): Type[];
// ---cut---
const arr = combine<string | number>([1, 2, 3], ["hello"]);

Guidelines for writing good generic functions

It's fun to write a generic function, and it can be easy to use type parameters.
Using too many type parameters or constraints where they are not strictly necessary can lead to poor reasoning and frustration with your function callers.

Pressing the type parameter

Here's how to write two functions that look similar.

function firstElement1<Type>(arr: Type[]) {
  return arr[0];
}

function firstElement2<Type extends any[]>(arr: Type) {
  return arr[0];
}

// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);

At first glance, it may look the same, firstElement1This is a better way to write this function.
The deferred return type of this function is Type However, firstElement2The inferred return type of is at the time of the call, rather than "waiting" for TypeScript to interpret the type during the call. arr[0] Because the expression is interpreted using type constraints, anybecomes.

rule: If possible, use the type parameters themselves rather than constraining them.

Using fewer type parameters

Here is another pair of similar functions:

function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
  return arr.filter(func);
}

function filter2<Type, Func extends (arg: Type) => boolean>(
  arr: Type[],
  func: Func
): Type[] {
  return arr.filter(func);
}

We are You don't associate two values. Type Parameters FuncI created it.
This is quite bad, because the caller who wants the type argument has to provide additional type arguments for no reason.
Funcwill only make the function harder to read and understand, it will do nothing!

rule: Always use the minimum type parameters if possible

The type parameter must appear twice.

Sometimes we overlook the fact that functions may not need generics.

function greet<Str extends string>(s: Str) {
  console.log("Hello, " + s);
}

greet("world");

We could have easily written a simple version.

function greet(s: string) {
  console.log("Hello, " + s);
}

The type parameter is _Associating multiple value types_Please remember that it is used for the purpose of doing.
If a type parameter is used only once in a function signature, it is not associated with anything.

rule: If the type parameter appears in only one place, think again about whether it is really necessary.

Optional Parameters

Functions in JavaScript often use a variable number of arguments.
For example, numberof toFixed The method optionally uses digits.

function f(n: number) {
  console.log(n.toFixed()); // 0 arguments
  console.log(n.toFixed(3)); // 1 argument
}

In TypeScript, we use the parameter to ?By marking it as _Optional_It can be made with

function f(x?: number) {
  // ...
}
f(); // OK
f(10); // OK

If the type of parameter is number, but not specified in JavaScript, the parameter is undefinedBecause it becomes, x The parameters are substantially number | undefined It will be a type.

You can use the parameters _Default_We can also provide.

function f(x = 10) {
  // ...
}

now fWithin the body of all undefined Argument 10Because it is replaced by xThe type of is numberIt will be.
When the parameter is optional, the caller undefined, mimicking the "missing" argument.

declare function f(x?: number): void;
// cut
// All OK
f();
f(10);
f(undefined);

Optional parameters in callback functions

Once you know about optional parameters and function type expressions, it's easy to make the following mistakes when writing a function that invokes callbacks.

function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i], i);
  }
}

People index?is usually intended to be used as an optional parameter, and you want both calls to be valid.

// @errors: 2532
declare function myForEach(
  arr: any[],
  callback: (arg: any, index?: number) => void
): void;
// ---cut---
myForEach([1, 2, 3], (a) => console.log(a));
myForEach([1, 2, 3], (a, i) => console.log(a, i));

indeed What this means is that callbackCan be called with this one argument Is.
In other words, the previous function definition is like saying that the implementation might look like this:

// @errors: 2532
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
  for (let i = 0; i < arr.length; i++) {
    // 오늘은 index를 제공하고 싶지 않아
    callback(arr[i]);
  }
}

Eventually, TypeScript enforces this semantics, resulting in errors that wouldn't actually happen.

// @errors: 2532
declare function myForEach(
  arr: any[],
  callback: (arg: any, index?: number) => void
): void;
// ---cut---
myForEach([1, 2, 3], (a, i) => {
  console.log(i.toFixed());
});

In JavaScript, if a call is made with more arguments than specified by the parameter, the remaining arguments are simply ignored.
TypeScript behaves the same way.
A function with fewer parameters (with the same type) can replace a function with more parameters.

When you write a function type for a callback, you can use the Call Unless there is an intent, never Do not use optional parameters.

Function Overload

Some JavaScript functions can be called with varying numbers and types of arguments.
For example, you can use Dateand take one timestamp (one argument), or you can create a function that takes month/day/year format (three arguments).

In TypeScript, we have a function that can be called in different ways. _Overlord Signature_It can be described as writing.
To do this, you need to write down a few function signatures (usually two or more) and then write the function body.

// @errors: 2575
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3);

In this example, we have created two overloads. One receives one argument, the other receives three arguments.
These two signatures from the beginning Overlord Signature It is called.

Then, we wrote a function implementation with a compatible signature.
The function is Implement It has a signature, but it cannot be called directly.
Although we have created two optional parameters after the required parameters, we cannot call this function with only two parameters!

Overlord Signatures and Implementation Signatures

This is a common source of confusion.
People often write code like the one below, and don't understand why there is an error.

// @errors: 2554
function fn(x: string): void;
function fn() {
  // ...
}
// 0개의 인자로 호출하기를 예상했음
fn();

Again, the signature used to write the function body is "invisible" from the outside.

_Implement_The signature of is invisible from the outside.
When you write an overloaded function, you can use the _More than one_You must write the signature of the function on top of the function implementation.

In addition, the implementation signature is an overloaded signature and Must be compatible The.
For example, the following functions are erroneous because the implementation signature does not correctly match the overloads.

// @errors: 2394
function fn(x: boolean): void;
// 인수 타입이 옳지 않습니다.
function fn(x: string): void;
function fn(x: boolean) {}
// @errors: 2394
function fn(x: string): string;
// 반환 타입이 옳지 않습니다.
function fn(x: number): boolean;
function fn(x: string | number) {
  return "oops";
}

Writing a good overload

As with generics, there are some guidelines to follow when writing function overloads.
Following these rules will make your functions easier to call, easier to understand, and easier to implement.

Consider a function that returns the length of a string or array.

function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
  return x.length;
}

This function is fine. We can call this function through a string or an array.
However, since TypeScript only interprets a function through one overload, we can use this function as a string or It cannot be called through values that can be arrays.

// @errors: 2769
declare function len(s: string): number;
declare function len(arr: any[]): number;
// ---cut---
len(""); // OK
len([0]); // OK
len(Math.random() > 0.5 ? "hello" : [0]);

Since both overloads have the same number of arguments and the same return type, we can write the following in the form of an unoverloaded function:

function len(x: any[] | string) {
  return x.length;
}

Much better!
The caller can call this function with one of two values, eliminating the need to find an additional exact implementation signature.

If possible, use the union type instead of overload

Within the function this To declare

TypeScript uses the thisWhat should be, we deduce through code flow analysis, as shown in the example below.

const user = {
  id: 123,

  admin: false,
  becomeAdmin: function () {
    this.admin = true;
  },
};

TypeScript functions user.becomeAdminThis external object userEquivalent to thisI understand that it has. Usually this may be enough, but you guys thisYou'll often need more control over what objects represent. In the JavaScript specification, thisSince it says that you can't have a parameter named ", TypeScript uses that grammar space in the body of the function thisAllows it to be used to define the type of .

interface User {
  id: number;
  admin: boolean;
}
declare const getDB: () => DB;
// ---cut---
interface DB {
  filterUsers(filter: (this: User) => boolean): User[];
}

const db = getDB();
const admins = db.filterUsers(function (this: User) {
  return this.admin;
});

This pattern is commonly used in callback-style APIs, which control when other objects call a function. In order to achieve this effect, it is necessary to use a non-arrow function. function You must use keywords.

// @errors: 7041 7017
interface User {
  id: number;
  isAdmin: boolean;
}
declare const getDB: () => DB;
// ---cut---
interface DB {
  filterUsers(filter: (this: User) => boolean): User[];
}

const db = getDB();
const admins = db.filterUsers(() => this.admin);

Other types to know about

When working with function types, there are a few additional types that appear frequently.
Like all types, you can use them anywhere, but they're especially relevant in the context of functions.

void

voidmeans the return value of a function that does not return a value.
In the function returnWhen there is no statement, or when it does not explicitly return a value, it is the type that is deduced.

// 추론된 반환 타입은 void 입니다.
function noop() {
  return;
}

In JavaScript, a function that returns nothing is implicitly undefined Returns a value.
But in TypeScript, voidand undefinedis not considered the same.
We'll go into more detail at the end of this chapter.

voidThe undefinedIt is not the same as.

object

Special type objectis the raw value (string, number, bigint, boolean, symbol, null, undefined) to any value that is not
This is Empty object type { }It is different from the global type ObjectIt is also different from .
Perhaps you ObjectYou won't be able to use it.

objectThe ObjectIt's not. All the time objectUse it!

In JavaScript, the value of a function is an object. There are properties, and in the prototype chain Object.prototypeThere is, instanceof ObjectIn the meantime, Object.keysand so on.
For this reason, in TypeScript, the function type is objectIt is considered as.

unknown

unknown The type is _All values_Indicates the
any It is similar to the type, unknown It is safer because it is not valid to substitute something for the type.

// @errors: 2571
function f1(a: any) {
  a.b(); // OK
}
function f2(a: unknown) {
  a.b();
}

This is because any It is useful for describing function types because it is possible to express a function that accepts any value without using the value of the form in the body of the function.

Conversely, you can represent a function that returns a value of type unknown.

declare const someRandomString: string;
// ---cut---
function safeParse(s: string): unknown {
  return JSON.parse(s);
}

// 'obj'를 사용할 때 조심해야 합니다!
const obj = safeParse(someRandomString);

never

What functions are Never It does not return a value

function fail(msg: string): never {
  throw new Error(msg);
}

never Type means a value that can never be observed.
In return type, it means that the function throws an exception or terminates program execution.

neveralso appears when TypeScript determines that there is nothing left in the union.

function fn(x: string | number) {
  if (typeof x === "string") {
    // do something
  } else if (typeof x === "number") {
    // do something else
  } else {
    x; // 'never' 타입이 됨!
  }
}

Function

Global Types Functionsilver bind, call, apply It is also used to describe other properties in JavaScript function values.
In addition, this includes FunctionThe value of the type has a value that can always be called, and these calls are anyReturns .

function doSomething(f: Function) {
  return f(1, 2, 3);
}

This is because Call an untyped function It is unsafe and unsafe any It's generally best to avoid them because they return the type.

If you need to allow an arbitrary function, but you don't want to call it, () => void The type is generally safer.

The remaining parameters and arguments

배경지식 읽기:
나머지 매개변수(Rest Parameter)
전개 구문(Spread Syntax)

Rest Parameter

We can use optional parameters and overloads to accept a variety of fixed arguments, but we can use Undefined A function that accepts a number argument _The rest of the parameters_It can be defined using .

The rest of the parameters appear after all other parameters, ... Use syntax.

function multiply(n: number, ...m: number[]) {
  return m.map((x) => n * x);
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4);

In TypeScript, the type notation for these parameters is implicitly anyRather than any[], and the type expression is Array<T> or T[] Or it should be expressed in tuple type (which we will learn later).

Rest Argument

Conversely, we use the deployment syntax to determine the number of arguments supplied by an array to _offer_I can.
For example, the array's push A method can take any number of arguments.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
arr1.push(...arr2);

In general, TypeScript does not consider arrays to be immutable.
This can cause the following surprising behavior:

// @errors: 2556
// 추론된 타입은 0개 이상의 숫자를 가지는 배열인 number[]
// 명시적으로 2개의 숫자를 가지는 배열로 간주되지 않습니다
const args = [8, 5];
const angle = Math.atan2(...args);

The best solution to this situation depends on the code, but in general, const Context is the simplest solution.

// 길이가 2인 튜플로 추론됨
const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);

Using the remaining arguments is important when targeting older runtimes, downlevelIterationYou may need to.

Parameter Destruction

배경지식 읽기:
구조분해 할당

Parameter decomposition can be used to conveniently unpack an object provided as an argument into one or more local variables in the body of the function.
In JavaScript, it looks like this:

function sum({ a, b, c }) {
  console.log(a + b + c);
}
sum({ a: 10, b: 3, c: 9 });

The type notation for the object is located after the decomposition syntax.

function sum({ a, b, c }: { a: number; b: number; c: number }) {
  console.log(a + b + c);
}

It may seem a bit verbose, but you can use the named type here as well.

// 이전 예제와 동일
type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
  console.log(a + b + c);
}

Assignability of functions

void Return Type

hydrous void Return types can cause some uncommon but predictable behavior.

void Contextual typing with return types returns nothing to the function Avoid Not forced IsAnother way to explain this is, void A contextual function type with a return type (type vf = () => void) is implemented, _No value_or may be returned, but will be ignored.

Therefore, the type to be described later () => voidAll implementations are valid.

type voidFunc = () => void;

const f1: voidFunc = () => {
  return true;
};

const f2: voidFunc = () => true;

const f3: voidFunc = function () {
  return true;
};

And when the return values of these functions are assigned to other variables, they are still voidWe will keep the type.

type voidFunc = () => void;

const f1: voidFunc = () => {
  return true;
};

const f2: voidFunc = () => true;

const f3: voidFunc = function () {
  return true;
};
// ---cut---
const v1 = f1();

const v2 = f2();

const v3 = f3();

Since such behavior exists, Array.prototype.pushreturns a number, Array.prototype.forEach Method void Even if you expect a function with a return type, the following code may be valid:

const src = [1, 2, 3];
const dst = [0];

src.forEach((el) => dst.push(el));

There is one other case to keep in mind. If the literal function definition is void If it has a return value, the function must return nothing. Not.

function f2(): void {
  // @ts-expect-error
  return true;
}

const f3 = function (): void {
  // @ts-expect-error
  return true;
};

voidFor more information, refer to the following article topics.

Generated by 🚫 dangerJS against 9d86fb2

@yusunghyun
Copy link
Contributor Author

@microsoft-github-policy-service agree

@dvlprsh
Copy link
Contributor

dvlprsh commented Jun 6, 2023

LGTM

@github-actions github-actions bot merged commit 8d7697f into microsoft:main Jun 6, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Jun 6, 2023

Merging because @dvlprsh 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.

2 participants