- μΈλΆλΌμ΄λΈλ¬λ¦¬λ index.d.ts νμΌμ λ§λ€μ΄μ μ¬μ©ν΄μΌ νλ€.
- λͺ¨λ νμΌμ TSλ‘ μμ±ν΄μΌ νλ€.
- tsconfig.json μΆκ° μ€μ
- noImplicitAny : true
- strictNullChecks : true
class Computer {
#number: number[];
constructor() {
this.#number = this.generate();
console.log(this.#number);
}
...
}
class Player {
#numbers: number[];
#ballCounts: { strike: number; ball: number; };
storeNumber(numbers: number[]) {
this.#numbers = numbers;
}
...
}
- νμ νμλ₯Ό μν λ°©λ²μΌλ‘λ νμ μ μΈκ³Ό νμ λ¨μΈ λ κ°μ§ λ°©λ²μ΄ μ‘΄μ¬νλ, νμ λ¨μΈμ΄ κΌ νμν κ²½μ°κ° μλλΌλ©΄, μμ μ± μ²΄ν¬λ λλ νμ μ μΈμ μ¬μ©νλκ²μ΄ μ’κΈ°λλ¬Έμ νμ μ μΈμ μ¬μ©νμμ΅λλ€.
- νμ λ¨μΈμ νμ μ²΄μ»€κ° νλ¨νλ νμ λ³΄λ€ μμ±μμΈ λ΄κ° νλ¨νλ νμ μ΄ λ μ νν λ μλ―Έκ° μλλ°, μ΄ κ²½μ°μλ ν΄λΉλμ§ μλλ€κ³ μκ°νμ΅λλ€.
readPlayerCommand(callback: Function) {
Console.readLine(GAME_MESSAGE.inGame, (input: string) => {
checkCorrectNumber(input);
callback(input);
});
}
- ν΄λΉ μ½λλ InputView λ΄λΆμ μ½λμΈλ°, νμ μ€ν¬λ¦½νΈμμλ ν¨μ λ¬Έμ₯κ³Ό ν¨μ ννμμ ꡬλΆν©λλ€.
- νμ μ€ν¬λ¦½νΈμμ κΆμ₯λλ μ¬νμ ν¨μ ννμμ μ¬μ©νλ κ² μ λλ€. μλνλ©΄ ν¨μ 맀κ°λ³μλΆν° λ°νκ°κΉμ§ μ 체λ₯Ό ν¨μ νμ μΌλ‘ μ¬μ©νμ¬ μ¬μ¬μ© ν μ μκΈ° λλ¬Έμ λλ€.
- λ°λΌμ κ°μ²΄ λ΄λΆ λ©μλλ‘ μ μΈν readPlayerCommand, κ·Έλ¦¬κ³ μ΄μ μ μ¬ν ννμ κΈ°λ₯λ€μ λͺ¨λ ν¨μ ννμμΌλ‘ λ°κΎΈμ΄μ£Όλκ² νμ μ€ν¬λ¦½νΈμμ κΆμ₯νλ λ°©λ²μΌλ‘ 보μ λλ€.
export const GAME_MESSAGE = Object.freeze({
start: 'μ«μ μΌκ΅¬ κ²μμ μμν©λλ€.',
inGame: 'μ«μλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ : ',
end: '3κ°μ μ«μλ₯Ό λͺ¨λ λ§νμ
¨μ΅λλ€! κ²μ μ’
λ£',
option: 'κ²μμ μλ‘ μμνλ €λ©΄ 1, μ’
λ£νλ €λ©΄ 2λ₯Ό μ
λ ₯νμΈμ.',
});
export const ERROR_MESSAGE = Object.freeze({
invalidNumber: '[ERR] μ ν¨νμ§ μμ μ
λ ₯μ
λλ€',
invalidCommand: '[ERR] μ ν¨νμ§ μμ μ’
λ£/μ¬μμ μ
λ ₯μ
λλ€',
});
- νμ μ€ν¬λ¦½νΈλ₯Ό λ¨Όμ μ ν κ°λ°μκ° κ°μ₯ λ§μ΄ νλ건 λͺ¨λ μ½λμ νμ ꡬ문μ λ£λ κ²μ΄λ, νμ μ²΄μ»€κ° μκΈ° λλ¬Έμ λͺ¨λ λ³μμ νμ μ μ μΈνλ건 λΉμμ°μ μ λλ€.
- νμ μΆλ‘ μ΄ λλ€λ©΄ λͺ μμ νμ ꡬ문μ νμνμ§ μμ΅λλ€. μ€νλ € λ°©ν΄κ° λ©λλ€.
- νμ μ€ν¬λ¦½νΈλ λ 볡μ‘ν κ°μ²΄ λν μΆλ‘ ν μ μκΈ°λλ¬Έμ νμ μ μ₯ν©νκ² λͺ¨λ λ¬μμ£Όλ κ² λ³΄λ¨ μΆλ‘ κ°λ₯ν νμ μ μ¬μ©νλκ² μ’μ΅λλ€.
class Computer {
#number: number[];
constructor() {
this.#number = this.generate();
console.log(this.#number);
}
...
}
- κ°μ²΄λ₯Ό μμ±ν λλ μμ±μ νλμ© μΆκ°ν기보λ€λ μ¬λ¬ μμ±μ ν¬ν¨ν΄μ νκΊΌλ²μ μμ±ν΄μΌ νμ μΆλ‘ μ μ 리ν©λλ€.
- μμ ν νμ
μΌλ‘ μμ±μ μΆκ°νλ €λ©΄ κ°μ²΄ μ κ°
({...a, ...b})
λ₯Ό μ¬μ©νλ©΄ λ©λλ€. - 쑰건λΆλ‘ μμ±μ μΆκ°νκ³ μΆλ€λ©΄ ? λ₯Ό μ¬μ©ν©μλ€.
declare module '@woowacourse/mission-utils' {
class Console {
static readLine(input: string, callback: Function): void;
static print(message: string): void;
static close(): void;
}
class Random {
static pickNumberInRange(start: number, end: number): number;
}
}
- νμ μ€ν¬λ¦½νΈλ₯Ό μ¬μ©νλ€λ³΄λ©΄ μλνν°μ λͺ¨λμμ μ΅μ€ν¬νΈλμ§ μμ νμ μ λ³΄κ° νμν κ²½μ°κ° μκΉλλ€
- λΆνμν anyλ₯Ό νΌνκΈ° μν΄μλΌλ νμ μ λͺ μμ μΌλ‘ μμ±ν©μλ€.
- κ³΅κ° api 맀κ°λ³μμ λμ΄λ μκ° ννμ λ ΈμΆλλ―λ‘ μ¨κΈ°λ €νμ§λ§κ³ λΌμ΄λΈλ¬λ¦¬ μ¬μ©μλ₯Ό μν΄ λͺ μμ μΌλ‘ νμ μ μ΅μ€ν¬νΈνλκ²μ΄ μ’μ΅λλ€.
// ν΄λμ€
class BaseBallController {
#computer: Computer;
#player: Player;
constructor() {
this.#player = new Player();
}
start = () => {
OutputView.printStart();
this.#computer = new Computer();
this.getPlayerCommand();
};
...
}
// import, export
import { Random } from "@woowacourse/mission-utils";
export default Computer;
- νμ μ€ν¬λ¦½νΈ μ»΄νμΌλ¬λ₯Ό μλ°μ€ν¬λ¦½νΈ νΈλμ€νμΌλ¬λ‘ μ¬μ©ν μ μμ΅λλ€.
- νμ μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈμ μμμ§ν©μ΄κΈ°λλ¬Έμ, μ΅μ λ²μ μ μλ°μ€ν¬λ¦½νΈ μ½λλ₯Ό μλ λ²μ μ μλ°μ€ν¬λ¦½νΈ μ½λλ‘ λ³νν μ μμ΅λλ€.
- λ°λΌμ λͺ¨λ μλ°μ€ν¬λ¦½νΈ λ¬Έλ²μ μ¬μ©νλκ²μ κΆμ₯ν©λλ€.
- ECMAScriptμ λͺ¨λ, νλ‘ν νμ λμ ν΄λμ€, var λμ let/const λ±κ³Ό κ°μ μμλ€μ΄ μμ΅λλ€.
// νμ΄ν ν¨μ
start = () => {
OutputView.printStart();
this.#computer = new Computer();
this.getPlayerCommand();
};
getPlayerCommand = () => {
InputView.readPlayerCommand(this.sendPlayerCommand);
};
sendPlayerCommand = (input: string) => {
this.#player.storeNumber(input.split('').map(Number));
this.playBall();
};
- this ν€μλλ μΌλ°μ μΈ λ³μλ€κ³Όλ λ€λ₯Έ μ€μ½ν κ·μΉμ κ°μ§κΈ°λλ¬Έμ, μλ°μ€ν¬λ¦½νΈμμ κ°μ₯ μ΄λ €μ΄ κ°λ μ€ νλμ λλ€.
- νμ΄ν ν¨μλ₯Ό μ¬μ©νλ©΄ μμ μ€μ½νμ thisλ₯Ό μ μ§ν μ μμ΅λλ€.
- μΌλ° ν¨μλ³΄λ€ νμ΄ν ν¨μκ° λ μ§κ΄μ μ΄λ©° μ½λλ κ°κ²°ν΄μ§κΈ° λλ¬Έμ κ°κΈμ νμ΄ν ν¨μλ₯Ό μ¬μ©νλ κ²μ΄ μ’μ΅λλ€.
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
"strictNullChecks": true
- νμ μ€ν¬λ¦½νΈ μ€μ μ 컀맨λ λΌμΈμ μ΄μ©ν기보λ€λ tsconfig.jsonμ μ¬μ©νλκ² μ’λ€.
- 'undefinedλ κ°μ²΄κ° μλλλ€' κ°μ λ°νμ μ€λ₯λ₯Ό λ°©μ§νκΈ° μν΄ strictNullChecksλ₯Ό μ€μ νλ κ²μ΄ μ’λ€'
- μλ°μ€ν¬λ¦½νΈ νλ‘μ νΈλ₯Ό νμ μ€ν¬λ¦½νΈλ‘ μ ννλ κ² μλλΌλ©΄ noImplicitAnyλ₯Ό μ€μ νλ κ²μ΄ μ’λ€
λ¬Έλ§₯μμΌλ‘ xλΌλ λ³μκ° λμμ Foo νμ κ³Ό Bar νμ μ ν λΉ κ°λ₯νλ€λ©΄, μ€λ₯λ₯Ό μ κ±°νλ λ°©λ²μ λ κ°μ§μ΄λ€.
function f1() {
const x: any = expressionReturningFoo(); // μ΄λ κ² x
processBar(x);
}
function f2() {
const x = expressionReturningFoo();
processBar(x as any); // μ΄κ² λ μ’μ
// why? λ€λ₯Έ μ½λμ μν₯ x
}
checkNumber(input: unknown) {
if (/\D/.test(input as any)) {
throw new ValidationError(ERROR_MESSAGE.only_number);
}
},
checkLength(input: string) {
if (input.length !== 1) {
throw new ValidationError(ERROR_MESSAGE.length_one);
}
},
- μλμΉ μμ νμ μμ μ±μ μμ€μ νΌνκΈ° μν΄ anyμ μ¬μ© λ²μλ₯Ό μ΅μνμΌλ‘ μ’νμΌ νλ€.
- ν¨μμ λ°ν νμ μ΄ anyμΈ κ²½μ° νμ μμ μ±μ΄ λλΉ μ§λ€. λ°λΌμ any νμ μ λ°ννλ©΄ μ λ μλλ€.
- κ°μ λ‘ νμ μ€λ₯λ₯Ό μ κ±°νλ λ°©λ²μ any λ§κ³ @ts-ignore λ κ°λ₯νλ€.
- unknownμ any λμ μ¬μ©ν μ μλ μμ ν νμ μ΄λ€. μ΄λ ν κ°μ΄ μμ§λ§ κ·Έ νμ μ μμ§ λͺ»νλ κ²½μ°λΌλ©΄ unknownμ μ¬μ©νλ©΄ λλ€.
- unknownμ μ¬μ©νλ €λ©΄ νμ λ¨μΈλ¬Έ or νμ 체ν¬λ₯Ό ν΄μΌνλ€.
class GameNumber {
#inputNumbers;
constructor(input: unknown) {
this.#inputNumbers = input;
this.#checkInput();
}
#checkInput() {
this.#checkNumber();
this.#checkZero();
this.#checkNumberOfDigits();
this.#checkDuplication();
}
#checkNumber() {
if (this.#isNotNumber()) throw new ValidationError(ERROR_MESSAGE.only_number);
this.#inputNumbers = Number(this.#inputNumbers);
}
#isNotNumber() {
return isNaN(Number(this.#inputNumbers));
}
#checkZero() {
if (this.#haveZero()) throw new ValidationError(ERROR_MESSAGE.not_zero);
}
#haveZero() {
if (typeof this.#inputNumbers === 'number') {
return [...this.#inputNumbers.toString()].includes('0');
}
}
const GameCommand = {
checkGameCommand(input: unknown) {
this.checkNumber(input);
this.checkLength(input as string);
this.checkOneOrTwo(input as string);
},
checkNumber(input: unknown) {
if (/\D/.test(input as any)) {
throw new ValidationError(ERROR_MESSAGE.only_number);
}
},
checkLength(input: string) {
if (input.length !== 1) {
throw new ValidationError(ERROR_MESSAGE.length_one);
}
},
checkOneOrTwo(input: string) {
if (!/1|2/.test(input)) {
throw new ValidationError(ERROR_MESSAGE.one_or_two);
}
},
};
export default GameCommand;
- μΆκ° 1 κ°λ unknwon λμ μ λλ¦ λ§€κ°λ³μκ° μ¬μ©λλ κ²½μ°λ μλ€.
function safeParseYAML(yaml: string): unknown {
return parseYAML(yaml);
}
function safeParseYAML<T>(yaml: string): T {
return parseYAML(yaml);
}
μ λλ¦μ μ¬μ©ν μ€νμΌμ νμ λ¨μΈλ¬Έκ³Ό λ¬λΌ 보μ΄μ§λ§ κΈ°λ₯μ μΌλ‘λ λμΌνλ€. μ λλ¦λ³΄λ€λ unknownμ λ°ννκ³ μ¬μ©μκ° μ§μ λ¨μΈλ¬Έμ μ¬μ©νκ±°λ μνλ λλ‘ νμ μ μ’νλλ‘ κ°μ νλ κ²μ΄ μ’λ€.
- μΆκ° 2
{} νμ λ μ‘΄μ¬νλλ° μ΄λ unknown 보λ€λ λ²μκ° μ½κ° μ’λ€.
{} νμ μ nullκ³Ό undefinedλ₯Ό μ μΈν λͺ¨λ κ°μ ν¬ν¨νλ€. {} νμ μ unknown νμ μ΄ λμ λκΈ° μ μλ {}κ° λ μΌλ°μ μΌλ‘ μ¬μ©λμλ€. μ΅κ·Όμ¬μ©μ λλ¬Όλ€.
let emptyObject: {} = 123; // μ«μ
emptyObject = 'hello'; // λ¬Έμμ΄
emptyObject = [1, 2, 3]; // λ°°μ΄
let emptyObject: {} = null; // μ€λ₯ λ°μ
let emptyObject: {} = undefined; // μ€λ₯ λ°μ
κ°μ ννμ ν΄λΉνλ νμ μ μ μνκ³ μΆμ λ λ°λ‘ interface λ§λ€ νμμμ΄ typeofλ₯Ό μ¬μ©νλ©΄ λλ€.
class BaseballController {
#view: typeof View;
#model: Model;
#validator: Validator;
const View = {
printStart() {
OutputView.printStart();
},
readGameNumbers(callback: callback) {
InputView.readLine(`${INPUT_MESSAGE.game_number}`, callback);
},
readGameCommand(callback: callback) {
InputView.readLine(`${INPUT_MESSAGE.game_command}`, callback);
},
printHint(value: IprintHint) {
OutputView.printHint(value);
},
printSuccess() {
OutputView.printSuccess();
},
printError(error: IError) {
OutputView.printError(error);
OutputView.printGameEnd();
},
finishGame() {
OutputView.finishGame();
},
};
export default View;
νμ μ’νκΈ°λ νμ μ€ν¬λ¦½νΈκ° λμ νμ μΌλ‘λΆν° μ’μ νμ μΌλ‘ μ§ννλ κ³Όμ μ λ§νλ€.
before
type Ivalidator = typeof Validator & {
[key:string] : (input:unknown) => void;
}
#validator: Ivalidator;
#hasErrorWhanCheckInput(numbers: string, inputName: string) {
try {
this.#validator[inputName](numbers);
} catch (error) {
return this.#handleError(error as ErrorWithCause);
}
}
after
#validator: typeof Validator;
type inputName = 'checkGameNumbers' | 'checkGameCommand';
#hasErrorWhanCheckInput(numbers: string, inputName: inputName) {
try {
this.#validator[inputName](numbers);
} catch (error) {
return this.#handleError(error as ErrorWithCause);
}
}