Skip to content

Commit e4a9ad1

Browse files
committed
add: 규칙 7개 추가
- Rule TP-3-1(권장): 가능하면 `type` 대신 `interface`를 사용하세요. - Rule TP-3-2(권장): 구조적 타이핑은 가능하면 `class` 대신 `interface` 혹은 `type`을 사용하세요. - Rule EX-1-10(필수): `==` 대신 `===`을 사용하세요. - Rule TP-5-1(권장): `any`보다는 `any[]`, `Record`를 사용하세요. - Rule CL-1-5(권장): `#` private 접근 제어자를 사용하지 마세요. - Rule CL-1-6(권장): 멤버 변수 선언 대신 생성자 매개 변수 선언을 적극 활용하세요. - Rule SD-1-1(필수): Deprecated 되거나 구식 기능을 사용하지 마세요.
1 parent 718f9a9 commit e4a9ad1

File tree

3 files changed

+213
-22
lines changed

3 files changed

+213
-22
lines changed

README.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 개요
44

5-
본 문서는 TypeScript와 TypeScript의 뿌리인 JavaScript에 대한 코딩 가이드라인에 대해 정리한 문서입니다. TypeScript의 기능만으로는 절대 하나의 실행 가능한 프로그램을 만들 수 없기에, TypeScript 뿐만 아니라 JavaScript의 코딩 가이드라인로 같이 담았습니다.
5+
본 문서는 TypeScript와 TypeScript의 뿌리인 JavaScript에 대한 코딩 가이드라인에 대해 정리한 문서입니다. TypeScript의 기능만으로는 절대 하나의 실행 가능한 프로그램을 만들 수 없기에, TypeScript 뿐만 아니라 JavaScript의 코딩 가이드라인으로 함께 담았습니다.
66

77
하나의 동작 가능한 프로그램을 위해 필요한 가장 작은 단위인 식별자, 타입부터 시작하여 표현식, 문, 함수 순으로 가이드라인을 작성했습니다.
88

@@ -43,10 +43,19 @@ PR을 올려주시거나 `Discussions` 메뉴을 통해서 수정, 추가 규칙
4343
타입스크립트(x), TypeScript(O)
4444
```
4545

46-
- 용어는 [MDN 용어 가이드](https://github.com/mdn/translated-content/blob/main/docs/ko/guides/glossary-guide.md)에 따릅니다.
46+
- 용어는 기본적으로 [MDN 용어 가이드](https://github.com/mdn/translated-content/blob/main/docs/ko/guides/glossary-guide.md)를 따릅니다.
47+
- 위 용어 가이드 중 아래 용어는 재정의(Override)합니다
48+
49+
```
50+
type: 형 -> 타입
51+
error: 오류 -> 에러
52+
syntax: 구문 -> 문법
53+
```
4754

4855
## 전체 규칙 목록
4956

57+
총 80개의 규칙이 있습니다.
58+
5059
1. 식별자(Identifier)
5160

5261
- ID-1-1(권장): 식별자는 영어 알파벳, 숫자, 언더바 만을 사용해야 합니다.
@@ -63,9 +72,12 @@ PR을 올려주시거나 `Discussions` 메뉴을 통해서 수정, 추가 규칙
6372
- TP-2-1(필수): `undefined``null`을 구분해서 사용하세요.
6473
- TP-2-2(필수): 타입 별칭에 `null`이나 `undefined`을 포함하지 마세요.
6574
- TP-2-3(필수): `| undefined` 대신 `?`을 사용하세요
75+
- TP-3-1(권장): 가능하면 `type` 대신 `interface`를 사용하세요.
76+
- TP-3-2(권장): 구조적 타이핑은 가능하면 `class` 대신 `interface` 혹은 `type`을 사용하세요.
6677
- TP-4-1(필수): `any`를 가능하면 사용하지 마세요.
6778
- TP-4-2(필수): `any`를 사용해야 하는 코드가 있다면 린트 경고를 끄고 사유를 적으세요.
6879
- TP-4-3(필수): `any`보다는 `unknown`을 사용하세요.
80+
- TP-5-1(권장): `any`보다는 `any[]`, `Record`를 사용하세요.
6981
- TP-6-1(권장): `string`, `boolean`, `number`, `new` 표현식으로 초기화된 변수나 매개변수에 대한 타입 명시는 생략합니다.
7082
- TP-6-2(필수): 반환 타입을 명시하세요
7183

@@ -81,6 +93,8 @@ PR을 올려주시거나 `Discussions` 메뉴을 통해서 수정, 추가 규칙
8193
- CL-1-2(권장): `public` 접근 제어자에 일관성이 있어야 합니다.
8294
- CL-1-3(필수): `toString()`를 가능하면 재정의하지 마세요.
8395
- CL-1-4(권장): 빈 생성자는 생략하세요.
96+
- CL-1-5(권장): `#` private 접근 제어자를 사용하지 마세요.
97+
- CL-1-6(권장): 멤버 변수 선언 대신 생성자 매개 변수 선언을 적극 활용하세요.
8498

8599
5. 표현식(Expression)
86100

@@ -93,6 +107,7 @@ PR을 올려주시거나 `Discussions` 메뉴을 통해서 수정, 추가 규칙
93107
- EX-1-7(필수): 전개 연산자 사용 시 같은 타입에 사용하세요.
94108
- EX-1-8(필수): 다차원 배열을 전개 연산자의 피연산자로 사용하지 마세요.
95109
- EX-1-9(참고): 객체 인스턴스 생성 이후에 속성을 추가하거나 제거하지 마세요.
110+
- EX-1-10(필수): `==` 대신 `===`을 사용하세요.
96111

97112
6. 문(Statement)
98113

@@ -123,6 +138,7 @@ PR을 올려주시거나 `Discussions` 메뉴을 통해서 수정, 추가 규칙
123138

124139
9. 표준 내장 객체 및 함수(Standard Built-in Objects/Functions)
125140

141+
- SD-1-1(필수): Deprecated 되거나 구식 기능을 사용하지 마세요.
126142
- SD-2-1(필수): `Array.prototype.map()`은 호출한 `Array`의 요소의 값 만 변경시켜야 합니다.
127143
- SD-2-2(필수): `Array.prototype.map()`의 반환 값을 반드시 사용하세요
128144
- SD-2-3(필수): `Array` 생성자를 사용하지 마세요

guide.md

+185-20
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,18 @@ const { some, _unused, variable } = obj;
109109
아래는 변수명으로서 안 좋은 예입니다.
110110

111111
```ts
112-
const whId = 1; // 내부에서만 사용하는 약어
113-
const n = 1; // 의미 없음
114-
const nErr = 1; // 모호한 약어
115-
const pcCounter = 1; // pc라는 약어에는 많은 의미가 있음
112+
const whId = 1; // 내부에서만 사용하는 약어
113+
const n = 1; // 의미 없음
114+
const nErr = 1; // 모호한 약어
115+
const pcCounter = 1; // pc라는 약어에는 많은 의미가 있음
116116
```
117117

118118
아래는 좋은 예입니다.
119119

120120
```ts
121-
const targetIp = '192.168.0.1'; // "IP"는 모두가 다 아는 약어
122-
const errorCount = 1; // 명확한 의미
123-
const warehouseId = 1; // 명확함
121+
const targetIp = '192.168.0.1'; // "IP"는 모두가 다 아는 약어
122+
const errorCount = 1; // 명확한 의미
123+
const warehouseId = 1; // 명확함
124124
```
125125

126126
예외: 10~15 줄 내의 작은 범위(Scope)에서 사용하거나 외부로 노출되지 않는 메서드, 변수의 인자명으로 쓸 경우 `n`, `i` 와 같이 짧은 변수명은 허용 가능하지만 해당 범위 내의 코드가 늘어날 경우 다시 의미가 모호해질 가능성이 있다는 점 유의하셔야 합니다.
@@ -176,8 +176,8 @@ function printLength(value: string) {
176176
console.log(value.length);
177177
}
178178

179-
printLength('hello'); // 정상 동작
180-
printLength(new String('hello')); // 컴파일 에러
179+
printLength('hello'); // 정상 동작
180+
printLength(new String('hello')); // 컴파일 에러
181181
```
182182

183183
위 코드에서는 두 번째 `printLength()` 호출 시 컴파일 에러가 발생합니다. 이를 고치려면 아래와 같이 불필요하고 번거롭게 타입 정의를 해야합니다.
@@ -263,7 +263,7 @@ const y: number | undefined = undefined;
263263
타입 별칭(`type`)에 union 타입으로 `null`이나 `undefined`이 포함되서는 안됩니다.
264264

265265
```ts
266-
// 안 좋은 코드
266+
// 안 좋은 코드
267267
type OperationSystem = 'Windows' | 'MacOS' | 'Linux' | undefined;
268268
```
269269

@@ -296,7 +296,64 @@ function buyHoody(address?: string): void {
296296

297297
### 2.3 구조적 타입(Structural Type)
298298

299-
TODO
299+
#### Rule TP-3-1(권장): 가능하면 `type` 대신 `interface`를 사용하세요.
300+
301+
현재 TypeScript에서 `type``interface`는 일면 비슷합니다. 하지만 버전 4.2 이전은 그렇지 않았습니다.
302+
303+
TypeScript 4.2 버전 이전에는 타입 별칭 선언(type alias declaration)을 사용하면 `.d.ts`출력이 훨씬 더 커지는 문제가 있었습니다. 타입 별칭 선언은 경우에 따라 타입 별칭의 내용이 중복해서 인라인으로 처리하는 반면 인터페이스는 항상 이름으로 참조되기 때문이었습니다.
304+
305+
현재는 `union`및 interchange 유형에 대한 유형 별칭을 보존하도록 수정했기 때문에 해당 문제는 사라졌고 일부 개발자들은 `interface`보다 `type`을 선호하기 시작했습니다.
306+
307+
하지만 `type`보다 `interface`를 먼저 고려해야하는 건 교차(intersection) 타입을 정의 및 사용할 때 입니다.
308+
309+
```ts
310+
interface A {
311+
x: number;
312+
}
313+
314+
interface B {
315+
x: string;
316+
}
317+
318+
interface C extends A, B {} // 에러 발생! (x의 타입 충돌)
319+
```
320+
321+
`interface`는 속성 충돌을 감지하는 단일 평면 객체 유형을 생성합니다. 반면 type은 속성을 재귀적으로 병합할 뿐, 어떤 경우에는 `never`를 생성합니다.
322+
323+
```ts
324+
type A = { x: number };
325+
type B = { x: string };
326+
327+
type C = A & B; // x는 never
328+
```
329+
330+
또한 `interface`의 타입 관계는 캐시되지만, 인터섹션 타입(`type & type`)은 전체를 다시 검사해야 합니다.
331+
332+
```ts
333+
interface A {
334+
foo: string;
335+
}
336+
337+
interface B extends A {}
338+
339+
const obj: B = { foo: 'hello' }; // 빠른 타입 검사 가능
340+
341+
type A = { foo: string };
342+
type B = { foo: string };
343+
type C = A & B;
344+
345+
const obj: C = { foo: 'hello' }; // 전체 타입을 다시 검사함
346+
```
347+
348+
이 때문에 성능 문제가 있을 수 있습니다.
349+
350+
이런 이유로 객체 타입 간의 교차 유형을 만들때는 `interface`를 사용하는게 좋습니다. 그리고 객체 타입 외 원시 타입, 튜플 타입, 객체 타입이 아닌 타입 간의 교차 타입은 타입 별칭을 사용하는게 좋습니다.
351+
352+
#### Rule TP-3-2(권장): 구조적 타이핑은 가능하면 `class` 대신 `interface` 혹은 `type`을 사용하세요.
353+
354+
`class`는 JavaScript, TypeScript 모두 지원하는 문법입니다. 따라서 TypeScript 를 컴파일해도 `class`는 그대로 결과물로 나온 `.js`파일에 들어갑니다. 하지만 `interface``type`은 타입 정보만 담고 있기 때문에 컴파일하면 결과물로 나온 `.js`에 들어가지 않습니다.
355+
356+
따라서 instanceof, 데코레이터나 리플렉션 등 런타임에서 해당 구조적 타입을 다룰 일이 있다면 사용해야하지만 단순히 구조적 타이핑 용도로만 사용한다면 `interface` 혹은 `type`을 사용하시기 바랍니다.
300357

301358
### 2.4 any
302359

@@ -351,7 +408,14 @@ if (typeof helloUnknown === 'string') {
351408

352409
### 2.5 타입 좁히기
353410

354-
TODO
411+
#### Rule TP-5-1(권장): `any`보다는 `any[]`, `Record`를 사용하세요.
412+
413+
`any`타입은 집합으로 치자면 전체 집합입니다. 모든 타입을 커버할 수 있죠. 그래서 `any`를 사용하면 어떠한 타입 시스템도 동작하지 않습니다. 하지만 만약 어떤 값이 들어올지 모르는 배열이라면? 문자열 키를 가졌지만 어떤 값이 올지 모르는 객체라면 그래도 타입을 좁힐 수 있습니다.
414+
415+
어떤 값이 들어올지 모르는 배열: `any[]`
416+
문자열 키를 가졌지만 어떤 값일지 모르는 객체: `Record<string, any>`
417+
418+
이렇게 사용하면 IDE의 도움을 받을 수 있고 최소한의 타입 시스템이 동작하게 됨으로서 버그를 예방할 수 있습니다.
355419

356420
### 2.6 타입 추론(Type Inference)
357421

@@ -622,6 +686,85 @@ class Foo3 {
622686
}
623687
```
624688

689+
#### Rule CL-1-5(권장): `#` private 접근 제어자를 사용하지 마세요.
690+
691+
ES2020(ES11)부터 도입된 JavaScript의 Private 필드(#)은 외부에서 절대 접근할 수 없고 클래스 내부에서만 접근할 수 있는 접근 제어자입니다. 얼핏 `private`과 비슷하지만 내부 동작 방식은 많이 다릅니다.
692+
693+
```ts
694+
class User {
695+
#privateValue: number;
696+
constructor(public name: string, public address: string) {}
697+
698+
getSecret(): number {
699+
return this.#privateValue;
700+
}
701+
}
702+
```
703+
704+
위와 같은 `User` 클래스가 있습니다. 큰 문제는 없어 보입니다. 그냥 `private` 처럼 동작하는거 아닌가 싶습니다. 하지만 다시 말씀드리지만 TypeScript는 JavaScript로 컴파일하기 때문에 그 동작을 보려면 실제 JavaScript로 컴파일된 결과를 봐야합니다.
705+
706+
```js
707+
use strict";
708+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
709+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
710+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
711+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
712+
};
713+
var _User_privateValue;
714+
class User {
715+
constructor(name, address) {
716+
this.name = name;
717+
this.address = address;
718+
_User_privateValue.set(this, void 0);
719+
}
720+
getSecret() {
721+
return __classPrivateFieldGet(this, _User_privateValue, "f");
722+
}
723+
}
724+
_User_privateValue = new WeakMap();
725+
```
726+
727+
여기서 `#`을 사용하면 안되는 이유가 나옵니다.
728+
729+
첫 번째로 `#` Private 필드는 클래스에 포함되지 않습니다. 즉 일반적인 객체처럼 객체 속성이어야 히든 클래스로 관리하는데 컴파일 한 결과는 `User` 클래스 외부에 `WeakMap`으로 정의되어 있습니다. 그렇기에 JavaScript 엔진이 `#` private 필드에 접근할 때는 일반적인 속성에 접근(예: this.propertyName) 할 때와는 달리 별도의 내부 매커니즘으로 호출해야기 때문에 성능이 저하됩니다.
730+
731+
두 번째로 `#` private 필드는 위 코드에서 보듯이 객체 외부에 정의되기 때문에 객체의 프로토타입 체인에 존재하지 않게 됩니다. 이 때문에 리플렉션이나 디버깅이 어렵습니다. 객체의 속성이 아니기에 `Object.keys()`, `JSON.stringify()`에도 해당 필드는 나타나지 않습니다.
732+
733+
세 번째로 일반적인 멤버와는 달리 별도의 `WeakMap`으로 관리하기 때문에 다량의 인스턴스를 만들 경우 일반적인 클래스보다 메모리 사용량이 증가합니다.
734+
735+
절대 외부에 해당 속성을 노출시키지 않아야 할 특별한 이유가 없는한 `#`Private 접근 제어자를 사용하지 마세요.
736+
737+
#### Rule CL-1-6(권장): 멤버 변수 선언 대신 생성자 매개 변수 선언을 적극 활용하세요.
738+
739+
TypeScript에서는 클래스 멤버 변수 선언 대신 생성자 매개 변수로 멤버 변수 선언이 가능합니다.
740+
741+
```ts
742+
class User {
743+
name: string;
744+
address: string;
745+
746+
constructor(name: string, address: string) {
747+
this.name = name;
748+
this.address = address;
749+
}
750+
}
751+
```
752+
753+
위 코드는 일반적인 클래스 사용법입니다. 멤버 변수를 선언하고 생성자에서 값을 초기화합니다.
754+
755+
```ts
756+
class User {
757+
constructor(name: string, address: string, private readonly age: number) {}
758+
}
759+
```
760+
761+
위 코드는 생성자 매개변수를 통해 멤버 변수를 선언하는 방식입니다. 이 방식을 사용하면 멤버 변수 선언과 초기화를 한 번에 함으로서 코드가 깔끔해지기도 하고 `public`, `private`, `readonly` 등의 접근 제한자와 함께 사용할 수 있습니다.
762+
763+
다만 아래의 경우에는 굳이 사용하실 필요는 없습니다.
764+
765+
- 생성자 로직이 복잡한 경우
766+
- 기본 값이 필요한 경우
767+
625768
## 5. 표현식(Expression, EX)
626769

627770
### 5.1 일반
@@ -847,7 +990,7 @@ const user2 = new User('박씨', '광주광역시'); // Hidden Class #1 (재사
847990

848991
위에서 `user1`, `user2`는 같은 히든 클래스를 사용합니다. 흡사 같은 틀을 사용하는 붕어빵이라고 보면 되겠습니다. 그렇기에 `user1.address` 접근은 기존에 만들어놓은 히든 클래스의 오프셋을 이용해서 바로 접근 가능합니다.
849992

850-
그런데, 여기서 동적으로 속성을 추가/삭제함으로서 위의 `User`의 히든 클래스의 틀이 깨진다면 어떻게 될까요? 어 저는 꼬리 없는 붕어빵이요. 저는 지느러미 없는 붕어빵이요. 주문이 들어올 때마다 틀을 다시 만들어야 합니다. 즉 새로운 히든 클래스를 만들게 되면 JIT 컴파일러가 최적화했던 코드를 다시 분석하며 이 과정에서 성능 저하가 발생합니다.
993+
그런데, 여기서 동적으로 속성을 추가/삭제함으로서 위의 `User`의 히든 클래스의 틀이 깨진다면 어떻게 될까요? 어 저는 꼬리 없는 붕어빵이요. 저는 지느러미 없는 붕어빵이요. 주문이 들어올 때마다 틀을 다시 만들어야 합니다. 즉 새로운 히든 클래스를 만들게 되면 JIT 컴파일러가 최적화했던 코드를 다시 분석하며 이 과정에서 성능 저하가 발생합니다.
851994

852995
```ts
853996
const user3 = new User('이씨', '부산광역시');
@@ -857,14 +1000,33 @@ const user3 = new User('이씨', '부산광역시');
8571000
TypeScript 코드에서는 동적으로 속성을 추가/삭제하는 일은 잘 없지만, 가장 문제되는 경우는 `Object`를 해시맵으로 사용하는 경우입니다. 속성을 추가할때마다 새로운 히든 클래스를 만들기에 성능 저하가 발생합니다.
8581001

8591002
```ts
860-
const objDict = {}; // 새로운 히든 클래스 생성
1003+
const objDict = {}; // 새로운 히든 클래스 생성
8611004
objDict.name = '한씨'; // 새로운 히든 클래스 생성
8621005
objDict.email = '[email protected]'; // 새로운 히든 클래스 생성
8631006
objDict.age = 99; // 새로운 히든 클래스 생성
8641007
```
8651008

8661009
이는 뒤에 나오는 `SD-2-10(권장): 해시맵이 필요할 때 Object대신 Record 혹은 Map을 사용하세요.` 규칙을 참고하세요.
8671010

1011+
#### Rule EX-1-10(필수): `==` 대신 `===`을 사용하세요.
1012+
1013+
JavaScript에서는 다른 프로그래밍 언어에도 있는 연산자인 `==` 연산자가 존재합니다. 거기에 덧붙여 `===` 연산자도 있습니다. `==``비교 연산자(Equality)`라 부르며 `===``엄격한 비교 연산자(Strict equality)`라고 합니다. 문젠 `=` 하나 차이인데 두 연산자의 동작이 매우 다릅니다.
1014+
1015+
`==`는 타입을 자동 변환해서 비교하지만, `===`는 타입 변환 없이 값과 타입을 모두 비교합니다. 이 자동 변환 과정때문에 프로그래머가 예상치 못한 결과를 받아볼 수 있습니다.
1016+
1017+
```ts
1018+
console.log(false == ''); // true -> 빈 문자열은 false로 변환
1019+
console.log(false === ''); // false -> 다른 타입
1020+
1021+
console.log(0 == '0'); // true -> 타입 변환 발생
1022+
console.log(0 === '0'); // false -> 다른 타입
1023+
1024+
console.log(null == undefined); // true -> 둘 다 "없음"을 의미한다고 간주
1025+
console.log(null === undefined); // false -> 다른 타입
1026+
```
1027+
1028+
따라서 언제나 `===`을 사용하세요. `==``===`의 비교표는 이 [링크](https://dorey.github.io/JavaScript-Equality-Table/)를 참고하세요.
1029+
8681030
## 6. 문(Statement, ST)
8691031

8701032
### 6.1 일반
@@ -1104,7 +1266,7 @@ const switchExample = (user: User): string => {
11041266

11051267
```ts
11061268
for (const prop in fooObj) {
1107-
// prop 은 부모 프로토타입일 수 있습니다
1269+
// prop 은 부모 프로토타입일 수 있습니다
11081270
}
11091271
```
11101272

@@ -1303,7 +1465,7 @@ function someFunc(p1: Foo, p2: Foo): number {
13031465
return p1.a + p1.a;
13041466
}
13051467

1306-
console.log(bar, bar2); // bar.a의 값 변경: { "a": -1, "b": 1 }, { "a": 5, "b": 5 }
1468+
console.log(bar, bar2); // bar.a의 값 변경: { "a": -1, "b": 1 }, { "a": 5, "b": 5 }
13071469
```
13081470

13091471
JavaScript와 달리 TypeScript에서는 `Readonly<T>` 유틸리티 타입을 사용하면 호출한 함수 내에서 매개 변수를 변경하지 못하게 할 수 있습니다. 이런 경우 매개 변수는 불변을 유지할 수 있기 때문에 예상치 못한 부작용을 막을 수 있습니다.
@@ -1378,7 +1540,7 @@ TypeScript 4.0부터 `catch` 절의 `error` 타입이 기본적으로 `unknown`
13781540
try {
13791541
throw 123; // 숫자를 throw
13801542
} catch (error) {
1381-
console.log(error.toUpperCase()); // 런타임 에러 발생
1543+
console.log(error.toUpperCase()); // 런타임 에러 발생
13821544
}
13831545
```
13841546

@@ -1412,7 +1574,7 @@ try {
14121574
callSomeFunc();
14131575
} catch (err: unknown) {
14141576
assertIsError(err);
1415-
console.error(err.message); // err이 Error라고
1577+
console.error(err.message); // err이 Error라고 간주
14161578
}
14171579
```
14181580

@@ -1424,7 +1586,10 @@ try {
14241586

14251587
#### Rule SD-1-1(필수): Deprecated 되거나 구식 기능을 사용하지 마세요.
14261588

1427-
TODO
1589+
JavaScript 표준이 아니거나, 지원이 중단된 기능을 사용하지 마세요.
1590+
1591+
- `with`문과 같이 더 이상 지원하지 않는 기능은 [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Deprecated_and_obsolete_features)에서 확인하실 수 있습니다.
1592+
- TC39에서 제안되었지만 아직 정식 기능이 아닌 기능을 사용하지 마세요. 2025.02.11 기준으로 예를 들면 `Temporal`이 있습니다.
14281593

14291594
### 9.2 표준 내장 객체
14301595

@@ -1526,7 +1691,7 @@ const b = [2, 3];
15261691
```ts
15271692
const someNumbers = [1, 2, 3];
15281693
const result = someNumbers.forEach((num) => {
1529-
return num * 2;
1694+
return num * 2; // ❌ 값 반환
15301695
});
15311696
```
15321697

0 commit comments

Comments
 (0)