숏컷 코드
function isString(value: unknown): value is string {
return typeof value === "string";
}
function assertString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error("expected string");
}
}좁히는 방식
이 카드에서 먼저 구분해 둘 기본형은 아래입니다.
- 분기용 predicate:
function isX(v): v is X - 보증용 assertion:
function assertX(v): asserts v is X - 전체 조건 assertion:
function assert(cond): asserts cond - 필터용 predicate:
(item): item is X => ...
1) 분기와 함께 narrowing하면 type predicate
value is T 반환 타입은 boolean을 돌려주면서, true일 때 값이 T라는 사실을 타입 시스템에 알려 줍니다.
function isString(value: unknown): value is string {
return typeof value === "string";
}조건문 안에서 안전하게 좁히고 싶을 때 가장 자연스럽습니다.
2) 실패 시 바로 중단하면 assertion function
asserts value is T는 false를 돌려주는 대신, 조건이 맞지 않으면 예외를 던지고 정상 복귀했다면 이후 코드를 T로 보게 합니다.
function assertString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error("expected string");
}
}검증 뒤 아래 코드 전체를 좁힌 상태로 진행하고 싶을 때 잘 맞습니다.
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}3) 차이는 호출 흐름에 있다
if (isString(input)) {
input.toUpperCase();
}
assertString(input);
input.toUpperCase();하나는 분기용, 하나는 보증용이라는 차이로 보면 가장 쉽습니다.
4) 배열 필터처럼 boolean 자리가 필요한 곳은 predicate가 자연스럽다
predicate는 boolean을 반환하므로 filter 같은 API에 그대로 넣기 좋습니다.
function isString(value: unknown): value is string {
return typeof value === "string";
}
const values: unknown[] = ["a", 1, "b"];
const strings = values.filter(isString); // string[]assertion function은 예외를 던지는 흐름이라 filter 같은 자리에 넣는 용도와는 잘 맞지 않습니다.
5) asserts condition은 invariant 보증에 잘 맞는다
꼭 특정 변수의 타입을 좁히지 않아도, "이 조건이 반드시 참이어야 아래로 진행한다"를 표현할 수 있습니다.
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
assert(user != null, "user required");
user.name;null 체크, config 필수값 확인, switch default 방어처럼 invariant를 강제할 때 자주 씁니다.
6) 타입 선언보다 검증 로직 정확성이 더 중요하다
이 함수들은 문법만 맞으면 되는 게 아니라, 실제 런타임 검증이 정확해야 합니다. 거짓말하는 predicate/assertion은 타입 시스템 전체를 속이는 결과가 됩니다.
7) 외부 입력 검증 래퍼에서 특히 강하다
JSON 파싱 결과, request body, config 객체 같은 외부 입력을 경계에서 검증하고 이후 코드를 단순화할 때 체감 이득이 큽니다.
- 조건문 안에서만 좁히면 된다: predicate
- 여기서 실패하면 아래 코드는 진행하면 안 된다: assertion
언제 어떤 선언을 쓸까
- true/false 분기와 함께 좁힌다: type predicate
- 실패 시 바로 throw한다: assertion function
- 이후 전체 블록을 좁힌 상태로 간다: assertion function
- 조건문 안에서만 좁히면 충분하다: type predicate
filter같은 boolean 자리에 넣는다: type predicate- null/invariant를 보증하고 아래로 진행한다:
asserts condition - 외부 입력 경계를 검증한다: 둘 다 유효, 흐름에 따라 선택
주의할 점
타입 선언만 맞고 실제 런타임 검증이 부정확하면, 타입 시스템을 속이는 결과가 됩니다. 이 함수들은 문법보다 검증 로직의 정확성이 더 중요합니다.
// ❌ 검증이 틀리면 위험
function isString(value: unknown): value is string {
return true;
}
// ✅ 실제 런타임 조건을 정확히 검사
function isString(value: unknown): value is string {
return typeof value === "string";
}참고 링크
1 sources