function ask() {
return prompt('When is your birthday?')
}
// 사용자가 유효한 내용을 입력하면 Date 반환
// 그렇지 않으면 null 반환
function parse(birthday: string): Date | null {
let date = new Date(birthday)
if (!isValid(date)) {
return null
}
return date
}
function isValid(date: Date) {
return Object.prototype.toString.call(date) === '[object Date]'
&& !Number.isNan(date.getTime())
}
let date = parse(ask())
if (date) {
console.info('Date is', date.toISOString())
} else {
console.error('Error parsing date for some reason')
}
...
function parse(birthday: string): Date {
let date = new Date(birthday)
if (!isValid(date)) {
throw new RangeError('Enter a date in the form YYYY/MM/DD')
}
return date
}
...
try {
let date parse(ask())
console.info('Date is', date.toISOString())
} catch (e) {
// 다른 에러가 발생했을 떄 무시하지 않도록, 처리하지 않은 에러 다시 던지도록 하기
if (e instanceof RangeError) {
console.error(e.message)
} else {
throw e
}
}
...
class InvalidDateFormatError extends RangeError { }
class DateIsInTheFutureError extends RangeError { }
/** 문서화 주석
* @throws {InvalidDateFormatError} 사용자가 생일을 잘못 입력함
* @throws {DateInTheFutureError} 사용자가 생일을 미래 날짜로 입력함
*/
function parse(birthday: string): Date {
let date = new Date(birthday)
if (!isValid(date)) {
throw new InvalidDateFormatError('Enter a date in the form YYYY/MM/DD')
}
if (date.getTime() > Date.now()) {
throw new DateIsInTheFutureError('Are you a timelord?')
}
return date
}
try {
let date parse(ask())
console.info('Date is', date.toISOString())
} catch (e) {
if (e instanceof InvalidDateFormatError) {
console.error(e.message)
} else if (e instanceof DateIsInTheFutureError) {
console.info(e.message)
} else {
throw e
}
}
: 유니온 타입을 이용해 자바의 throw문을 비슷하게 흉내냄
...
function parse(birthday: string): Date | InvalidDateFormatter | DateIsInTheFutureError {
let date = new Date(birthday)
if (!isValid(date)) {
return new InvalidDateFormatError('Enter a date in the form YYYY/MM/DD')
}
if (date.getTime() > Date.now()) {
return new DateIsInTheFutureError('Are you a timelord?')
}
return date
}
// => 메서드를 사용할 때 세 가지의 상황을 처리해야지 않으면 컴파일 타임에 TypeError 발생
...
let result = parse(ask()) // 날짜 또는 에러
if (result instanceof InvalidDateFormatError) {
console.error(result.message)
} else if (result instanceof DateInTheFutureError) {
console.info(result.message)
} else {
console.info('Date is', date.toISOString())
}
// 에러를 개별적으로 처리하지 않고 한번에 처리
let result = parse(ask()) // 날짜 또는 에러
if (result instanceof Error) {
console.info(result.message)
} else {
console.info('Date is', date.toISOString())
}
: 특수 목적 데이터 타입(NPM 설치 필요 or 직접 구현해야 함)
-- ex) Try, Option, Either 타입
: 어떤 특정 값을 반환하는 대신 값을 포함하거나 포함하지 않을 수도 있는 컨테이너를 반환
-- 컨테이너는 자체적으로 몇 가지 메서드를 제공
장점
: 성공하거나 실패할 수 있는 연산을 연달아 수행할 때 유용하게 사용 가능
: 오버로드 기능을 추가하면 Some과 None으로만 제한하여 코드를 훨씬 아전하게 만들 수 있음
단점
: 이런 데이터 타입을 사용하지 않는 다른 코드와는 호환되지 않음
: None으로 실패를 표현하기 때문에 실패한 이유를 자세히 알려주지 못함
// step1) 기본 정의
interface Option<T> { }
class Some<T> implements Option<T> {
constructor(private value: T) { }
}
class None implements Option<never> { }
// step2) 연산 정의
interface Option<T> {
// 비어있을 수도 있는 Option에 연산을 연쇄적으로 수행하는 수단
flatMap<U>(f: (value: T) => Option<U>): Option<U>
// Option에서 값을 가져옴
getOrElse(value: T): T
}
// step3) 메서드 구현
class Some<T> implements Option<T> {
constructor(private value: T) { }
flatMap<U>(f: (value: T) => Option<U>): Option<U> {
return f(this.value)
}
getOrElse(): T {
return this.value
}
}
class None implements Option<never> {
flatMap<U>(): Option<U> {
return this
}
getOrElse<U>(value: U): U {
return value
}
}
// step4) 시그니처 오버로드
interface Option<T> {
flatMap<U>(f: (value: T) => None): None
flatMap<U>(f: (value: T) => Option<U>: Option<U>
getOrElse(value: T): T
}
class Some<T> implements Option<T> {
constructor(private value: T) { }
flatMap<U>(f: (value: T) => None): None
flatMap<U>(f: (value: T) => Some<U>): Some<U>
flatMap<U>(f: (value: T) => Option<U>): Option<U> {
return f(this.value)
}
getOrElse(): T {
return this.value
}
}
class None implements Option<never> {
flatMap<U>(): None {
return this
}
getOrElse<U>(value: U): U {
return value
}
}
// step 5) 함수 구현 + 시그니처 오버로드
function Option<T>(value: null | undefined): None
function Option<T>(value: T): Some<T>
function Option<T>(value: T): Option<T> {
if(value == null) {
return new None
}
return new Some(value)
}
// 사용 예시
let result = Option(6) // Some<number>
.flatMap(n => Option(n * 3)) // Some<number>
.flatMap(n => new None) // None
.getOrElse(7) // 7