[Typescript] 아직도 Try - Catch 를 쓰시나요?

Falcon·2023년 3월 23일
2

typescript

목록 보기
2/6
post-thumbnail

Native Async (Promise) API

기존 async/await 을 쓸경우 다음과 같이 쓸 수 있다.

export async function isOddNum(num: number): Promise<boolean> {
    if (typeof num !== "number") throw Error('Invalid parameter type')
    if (num < 1) throw Error('Must be number >= 1')

    return num % 2 === 1
}

originPromise.test.ts

describe('Origin Promise Odd', ()=>{
    test('Success with one', async ()=>{
        const isOdd = await isOddNum(3)
        expect(isOdd).toBeTruthy()
    })

    test('Fail with zero', async ()=>{
        const isOdd = await isOddNum(0)
        // ⚠️ Error occurs since it can't receive error from 'throw'
        expect(isOdd).toBeFalsy()
    })
})

아쉽지만 아래 케이스에서 에러가 발생한다.
이는 Promise 에서 reject() 하거나 async 함수에서 throw() 할 때 다음과 같이 같이 에러를 핸들링 해줘야하기 때문이다.

  • Promise -> reject() -> .catch()
  • Async -> throw -> try ~ catch()

위 코드를 정상작동 시키기 위해 다음과 같이 개선할 수 있다.

    test('Fail with zero', async ()=>{
        const isOdd = await isOddNum(0)
        try {
            expect(isOdd).toBeFalsy()
          // ✅ catch 로 Error handling
        } catch (e) {
            console.error(e)
        }
    })

문제가 해결된 것 같은가?
그럼 우린 항상 async/await 을 쓸 때마다 try ~ catch 를 받아야하나?

throw - catch 없는 깔끔한 코드를 위해

async/await - try catch 코드는 안전하긴 하지만 코드가 간결하다고 보긴 어렵다. 이런 해결하기 위해 neverhrow 에서 해법을 제시한다.

우선 neverthrow 를 설치하라

$ yarn add neverthrow

neverthrow.isodd.ts

export function isOddNumWithNeverThrow(num: number): ResultAsync<boolean, Error> {
    if (typeof num !== "number") return errAsync(new Error('Invalid parameter type'))
    if (num < 1) return errAsync(new Error('Must be number >= 1'))

    return okAsync(num % 2 === 1)
}

neverthrow.test.ts

describe('isOddCheck with fromPromise',()=>{
    test('Success with one',async ()=>{
        const result = await isOddNumWithNeverThrow(1)
        expect(result.isOk()).toBeTruthy()
        expect(result.isErr()).toBeFalsy()
    })
    test('Fail with zero', async ()=>{
        const result = await isOddNumWithNeverThrow(0)
        expect(result.isOk()).toBeFalsy()
        expect(result.isErr()).toBeTruthy()
    })
})

try ~ catch 구문이 사라지고 isOk(), isErr() 메소드로 indent 없이 위에서 아래로 읽히는 코드로 개선됐다.

neverthrow/index.d.ts

declare class ResultAsync<T, E> implements PromiseLike<Result<T, E>> {
    // ..
    static fromSafePromise<T, E = never>(promise: PromiseLike<T>): ResultAsync<T, E>;
    static fromPromise<T, E>(promise: PromiseLike<T>, errorFn: (e: unknown) => E): ResultAsync<T, E>;
  // ..
}

리턴 타입이 ResultAsync<T,E> 다.

근데, PromiseLike 은 뭔가요?

옛날 Javascript 스팩에는 Promise API 가 공식적으로 존재하지 않고, 콜백을 통해 비동기를 처리했었다.
Promise API 를 제공하기 위한 서드파티 라이브러리가 있었는데 대표적인 예가 다음 2개다.

PromiseLike 과 Promise 의 차이점은 .catch 의 유무다. PromiseLike 은 오로지 .then() 으로만 결과를 처리했었다.


📝 결론

async/await - try/catch 코드에서 neverthrow 에서 제공하는 API 로 가독성 높은 코드를 작성하는 것을 고려해보자.

Reference

더 자세한 설명을 원한다면 이 neverthrow - wiki 를 참고하자.

profile
I'm still hungry

0개의 댓글