TypeScript의 keyof, typeof, T[K]는 어떻게 다르고 함께 쓰나요?
keyof는 객체 타입의 키 목록, 타입 위치의 typeof는 실제 값의 타입을 얻습니다. T[K]는 선택한 속성의 타입입니다. 상수 객체에 keyof typeof를 조합하면 키 이름을 다시 작성하지 않아도 됩니다.
본문 근거: TypeScript keyof 공식 설명
직접 확인: 오류에서 수정까지
TypeScript keyof typeof 인덱스 접근 타입 사용법 · 독립 실습
얻을 결과: keyof Task로 키를 제한해 오타 키를 컴파일 단계에서 찾습니다.
예제 크게 보기 (새 탭)단독 검사는 tsconfig.json이 없는 새 폴더에서 example.ts 한 파일씩 교체하며 진행합니다. TypeScript가 없다면 설치 글에서 Node.js/npm·TypeScript 준비를 확인하세요. 타입 검사 성공은 코드 실행 성공과 구분합니다.
독자 질문: Task의 실제 필드명만 받는 타입은 어떻게 만들까요?
얻을 결과: keyof Task로 키를 제한해 오타 키를 컴파일 단계에서 찾습니다.
오류 코드를 별도 example.ts에 복사합니다.
type Task = { id: string; title: string };
const key: keyof Task = 'titel';
예상 타입 오류 (TS2820): ‘titel’은 ‘id’ | ‘title’에 할당할 수 없습니다. 실제 속성명 title로 수정합니다.
위 코드를 다음 수정 예제로 교체합니다.
type Task = { id: string; title: string };
const key: keyof Task = 'title';
const task: Task = { id: '1', title: '공부' };
console.log(task[key]);
npx tsc --strict --noEmit example.ts
기존 최소 예제 확인 기록: TypeScript 7.0.2의 별도 최소 예제에서 오류 TS2820, 수정 후 exit 0 확인. TaskFlow 전체 체크포인트·브라우저 실행은 미확인.
다음 학습: 08회차에서 as const로 필터 값을 literal union으로 만듭니다.
keyof
type Task = {
id: string;
title: string;
completed: boolean;
};
type TaskKey =
keyof Task;
TaskKey는 다음 union이 됩니다.
'id' | 'title' | 'completed'
indexed access
특정 property의 타입을 가져올 수 있습니다.
type TaskTitle =
Task['title'];
TaskTitle은 string입니다.
여러 key:
type TaskValue =
Task[
'title' |
'completed'
];
결과는 string | boolean입니다.
typeof
JavaScript 값에서 타입을 추출합니다.
const settings = {
compact: false,
showCompleted: true,
};
type Settings =
typeof settings;
객체 shape를 한 번 더 직접 작성할 필요가 없습니다.
keyof typeof
const labels = {
all: '전체',
active: '진행 중',
completed: '완료',
};
type FilterKey =
keyof typeof labels;
결과:
'all' | 'active' | 'completed'
배열 element 타입
const priorities = [
'low',
'medium',
'high',
] as const;
type Priority =
typeof priorities[number];
Priority는 literal union이 됩니다.
as const는 다음 회차에서 자세히 다룹니다.
Generic helper 예고
keyof를 이용하면 존재하는 property만 허용하는 함수를 만들 수 있습니다.
function getValue<
T,
K extends keyof T,
>(
object: T,
key: K,
): T[K] {
return object[key];
}
제네릭 제약은 11회차에서 자세히 다룹니다.
독립 예제
const messages = {
loading:
'불러오는 중',
empty:
'데이터 없음',
error:
'오류 발생',
};
type MessageKey =
keyof typeof messages;
function getMessage(
key: MessageKey,
) {
return messages[key];
}
존재하지 않는 key는 받을 수 없습니다.
언제 typeof를 사용할까
값이 실제 원천이라면 typeof가 유용합니다.
설정 객체
상수 map
route map
status label
반대로 데이터 구조가 먼저 설계되고 여러 값이 그 타입을 따라야 한다면 type을 먼저 선언하는 편이 자연스러울 수 있습니다.
이 절의 typeof는 타입 위치에서 값의 타입을 참조하는 문법입니다. JavaScript 표현식에서 typeof value가 실행 중 “string” 같은 문자열을 반환하는 연산자와 사용 위치·결과가 다릅니다.
원리 확인: TypeScript keyof 공식 설명 (2026-10-12). 아래 기대 결과와 기존 실행 기록은 구분해서 읽습니다.
학습 완료 체크리스트
같은 Task에서 keyof Task와 Task[“title”]이 각각 무엇을 뜻하는지 설명해 보세요. 값인 labels에서 키를 얻을 때 typeof가 필요한 이유와 키를 추가하면 union이 함께 바뀌는 흐름을 확인합니다.
작은 변형으로 확인하기
Task에 completed: boolean을 추가하면 keyof Task에도 completed가 포함됩니다. key를 completed로 지정하고 객체에도 completed 값을 추가해 검사하세요.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 JavaScript 코드를 TypeScript로 확장합니다.
TaskFlow의 filter label과 Task property helper에서 타입 중복을 줄입니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
TypeScript keyof typeof 인덱스 접근 타입 사용법 · 단계별 TaskFlow
타입을 적용한 TaskFlow에서 작업을 추가·완료 전환·삭제하세요. 타입만 바뀐 단계는 이전과 화면이 같습니다.
예제 크게 보기 (새 탭)시작·완료 파일과 적용 순서에서 이번 변경 파일의 전체 내용과 적용 순서를 확인합니다.
시작본과 이번 변경
TypeScript 타입 좁히기: union null unknown 안전 검증의 완료 체크포인트를 기준으로 이어갑니다. 변경하지 않는 파일은 그 시작 상태를 유지합니다.
src/constants/task.ts의 taskLabels에서 keyof typeof로 TaskFilter를 파생합니다. TaskKey와 TaskTitle은 Task 타입을 import한 파일에서 선언합니다. 라벨 키를 추가했을 때 union에 반영되는지 확인합니다.
이전 체크포인트
typescript-step-06-narrowing
이번 목표
TaskFlow의 filter label과 Task property helper에서 타입 중복을 줄입니다.
전체 파일·코드 보기
src/constants/task.ts
export const taskLabels = {
all: '전체',
active: '진행 중',
completed: '완료',
};
export type TaskFilter =
keyof typeof taskLabels;
이제 key를 별도 union으로 다시 작성하지 않습니다.
Task key
import type {
Task,
} from '../types/task';
export type TaskKey =
keyof Task;
property 타입
export type TaskTitle =
Task['title'];
실제 코드에서 이름을 붙일 가치가 있을 때만 별도 alias를 만듭니다.
검증
npx tsc --noEmit
중간부터 시작하는 전체 파일과 적용 순서
시작 ZIP은 직전 프로젝트 단계 typescript-step-06-narrowing의 실제 완료 파일입니다. 프로젝트 순서를 이어가는 기준이며 사이트의 기술 읽기 순서와는 구분합니다.
- 시작 ZIP을 새 폴더에 풉니다. 이 폴더가 작업 루트이며 기존 체크포인트 파일을 별도로 찾을 필요가 없습니다.
- 필터 라벨 객체에서 TaskFilter를 파생하고 task-key.ts에서 keyof·인덱스 접근 타입을 공개합니다.
- 아래 변경 파일을 같은 상대 경로에 추가하거나 전체 교체합니다. 표시하지 않은 파일과 images는 시작본 그대로 유지합니다. package-lock.json은 완료 ZIP에 있는 파일을 함께 적용합니다.
- 작업 루트에서 명령을 실행하고 예상 결과를 확인합니다. 완료 ZIP은 직접 적용한 결과를 비교하는 기준입니다.
추가·교체 파일
CHANGES.md— 전체 교체README.md— 전체 교체tests/contracts.ts— 전체 교체src/constants/task.ts— 추가src/types/task-key.ts— 추가
실행과 확인
npm ci
npm run dev
npm run typecheck
npm test
npm run build
개발 서버는 실행 상태로 두고 다른 터미널에서 검사 명령을 실행하세요. Node.js 24.18.0에서 검증했습니다. JavaScript는 http://127.0.0.1:4173, TypeScript는 Vite 실행 출력의 주소로 접속합니다.
예상 결과: 없는 키는 컴파일에서 거부되며 TaskTitle은 string입니다.
기존 ZIP 검증 기록: TypeScript strict 검사·긍정/부정 타입 계약·런타임 검사·Vite 빌드 통과. 시작 ZIP에 이 글의 변경 파일만 적용한 결과와 완료 ZIP의 모든 파일 내용이 일치했습니다. 브라우저 화면 배치·실제 기기·외부 CDN 가용성은 이 검사에 포함하지 않았습니다.
이 절의 전체 파일은 ZIP과 일치합니다. 위쪽 원문의 짧은 예제나 생략 표시가 있는 부분 코드는 개념·변경 위치 설명용입니다. 서버 저장과 인증은 제공하지 않으며 JSON fixture는 로컬 읽기 실습용입니다.
복사할 변경 파일 전체 코드
tests/contracts.ts — 전체 파일
import type { Task } from '../src/types/task.js';
const valid: Task = {id:'1',title:'공부',completed:false};
// @ts-expect-error completed must be boolean
const invalid: Task = {id:'1',title:'공부',completed:'no'};
void valid; void invalid;
import { readTitle } from '../src/core/selection.js';
const nullable = readTitle(null);
// @ts-expect-error null result must be narrowed
const definite: string = nullable;
void definite;
import type { TaskKey } from '../src/types/task-key.js';
// @ts-expect-error nonexistent property
const badKey: TaskKey = 'missing';
void badKey;
src/constants/task.ts — 전체 파일
export const taskLabels = {
all: '전체',
active: '진행 중',
completed: '완료',
};
export type TaskFilter =
keyof typeof taskLabels;
src/types/task-key.ts — 전체 파일
import type {
Task,
} from '../types/task.js';
export type TaskKey =
keyof Task;
export type TaskTitle =
Task['title'];
완료 체크포인트
typescript-step-07-keyof-typeof
다음 학습
다음 글에서는 as const를 이용해 상수 배열과 객체의 literal 타입을 보존하는 방법을 배웁니다.
다음 글: TypeScript as const 사용법: union 타입을 자동 생성하는 기준
글 소개·학습 준비·전체 목차

TypeScript · 7 / 22 · 필수 학습 · keyof와 typeof
이미 있는 값·타입에서 키와 속성 타입을 어떻게 꺼낼까요? keyof는 타입의 키 union, 타입 위치의 typeof는 실제 값의 타입, T[K]는 선택한 속성의 타입을 얻습니다. 상수 객체에서 keyof typeof를 조합하면 키 목록을 다시 작성하지 않아도 됩니다.
이번 글에서 배우는 내용
- keyof
- typeof
- keyof typeof
- indexed access type
- 배열 element 타입 추출
- 타입 중복 줄이기
단독 학습 선수 지식
객체 타입과 상수 객체, property 접근을 알면 됩니다. 이전 TaskFlow 파일 없이 직접 확인 예제로 학습할 수 있습니다.
목차
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 22개 · 필수 21개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.