TypeScript 인터페이스와 유형: 실제 차이점, 컴파일러로 테스트

  1. 홈
  2. 블로그
  3. 소프트웨어 개발

소프트웨어 개발

TypeScript 인터페이스와 유형: 컴파일러로 테스트한 실제 차이점

인터페이스와 유형 별칭이 너무 많이 겹쳐서 선택이 임의적이라고 느껴지는 경우가 많습니다. 다음은 서로가 할 수 없는 작업을 실제 컴파일러 오류로 확인하고 팀이 따를 수 있는 간단한 규칙입니다.

읽는 시간 8분
TypeScript 인터페이스와 유형: 실제 차이점, 컴파일러로 테스트됨

핵심 요약

  • 일반 객체 모양의 경우 인터페이스와 유형 별칭은 거의 상호 교환 가능합니다.
  • type만이 공용체, 튜플 및 기타 비객체 유형의 이름을 지정할 수 있습니다.
  • interface만이 두 선언이 결합되는 선언 병합을 지원합니다.
  • 일반적인 규칙: 객체 모양에 대한 인터페이스,type다른 모든 것에는 일관되게 적용됩니다.

TypeScript 인터페이스와 유형만큼 많은 논쟁을 불러일으키는 TypeScript 질문도 없습니다. 많은 기사에는 이전 버전에서는 중요했지만 이제는 오래된 차이점이 나열되어 있습니다. 그래서 우리는 주장을 반복하기보다는 이를 테스트했습니다.

각 차이점을 연습하는 파일을 하나 작성하고 엄격 모드에서 TypeScript 5.6.3으로 확인했습니다. 아래에 인용된 컴파일러 오류는 해당 실행에서 발생합니다. 동작은 버전마다 변경될 수 있으므로 프로젝트에서 사용하는 TypeScript 버전을 확인하세요. 언어를 처음 사용하는 경우 초보자를 위한 TypeScript 튜토리얼부터 시작하세요.

짧은 대답

interface은 객체의 모양을 설명하며 확장되거나 다시 열릴 수 있습니다. type 별칭은 객체, 공용체, 튜플 및 함수 유형을 포함하여 모든 유형에 이름을 제공합니다. 둘 다 작동할 수 있는 경우 차이점은 대부분 스타일입니다.

같은 곳

다음 두 선언은 동일한 모양을 설명합니다.

interface User {
  id: number;
  name: string;
  email?: string;
}

type UserAlias = {
  id: number;
  name: string;
  email?: string;
};

const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a;   // no error

TypeScript는 구조적 타이핑을 사용합니다. 즉, 이름보다는 모양을 비교합니다. 이것이 바로 User를 불만 없이 UserAlias에 할당할 수 있는 이유입니다. 둘 다 선택적 속성, readonly 속성, 메서드 및 인덱스 서명을 지원합니다.

인터페이스와 유형을 나란히 비교

특징 인터페이스 유형
객체 모양 예 예
"a" | "b"과 같은 연합 아니요 예
튜플과 프리미티브 아니요 예
매핑된 유형과 조건부 유형 아니요 예
선언 병합 예 아니요, 오류입니다.
확장 연장하다 & 교차로
충돌하는 속성 선언 오류 조용히 될 수 있다 never
implements과 함께 사용됨 예 예, 객체 유형인 경우

타입만이 할 수 있는 일

많은 유용한 유형은 전혀 객체 모양이 아니며 유형 별칭만 이름을 지정할 수 있습니다.

type Status = "pending" | "paid" | "shipped";   // union
type Point = [number, number];                    // tuple
type Handler = (event: string) => void;           // function type
type Keys = keyof User;                           // computed type
type ReadonlyUser = Readonly<User>;               // utility type

공용체 유형은 TypeScript의 가장 중요한 기능 중 하나입니다. 왜냐하면 컴파일러는 목록 외부의 모든 값을 거부하기 때문입니다.

let s: Status = "paid";
s = "refunded";

// error TS2322: Type '"refunded"' is not assignable to type 'Status'.

이를 통해 프로덕션에 도달하기 전 컴파일 타임에 오타와 오래된 값을 찾아냅니다.

선언 병합

동일한 이름의 인터페이스를 두 번 선언하면 TypeScript는 이를 하나로 병합합니다.

interface Settings {
  theme: string;
}
interface Settings {
  language: string;
}

const settings: Settings = { theme: "dark", language: "en" };   // no error

유형 별칭을 사용하여 동일한 작업을 시도하면 컴파일러는 다음을 거부합니다.

type Config = { theme: string };
type Config = { language: string };

// error TS2300: Duplicate identifier 'Config'.

병합은 라이브러리를 통해 유형을 확장할 수 있는 방법입니다. 예를 들어, 라이브러리를 수정하지 않고도 전역 Window 인터페이스 또는 프레임워크의 요청 객체에 사용자 정의 속성을 추가할 수 있습니다. 정말 유용합니다.

반대 측면은 병합이 우연히 발생할 수 있다는 것입니다. 프로젝트의 두 파일이 동일한 범위에서 동일한 이름을 가진 인터페이스를 선언하는 경우 자동으로 결합되므로 혼란스러울 수 있습니다. 유형 별칭은 해당 충돌을 오류로 만듭니다.

확장 대 교차점

둘 다 기존 유형을 기반으로 구축할 수 있습니다.

interface Admin extends User {
  permissions: string[];
}

type AdminAlias = UserAlias & { permissions: string[] };

호환되는 속성의 경우 결과는 동일합니다. 차이점은 충돌으로 나타납니다. 인터페이스가 속성 유형을 비호환적으로 변경하려고 하면 컴파일러는 이를 즉시 보고합니다.

interface Base { id: number }
interface Broken extends Base { id: string }

// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
//   Types of property 'id' are incompatible.
//     Type 'string' is not assignable to type 'number'.

교차로가 선언될 때 불평하지 않습니다. 대신 충돌하는 속성은 어떤 값도 가질 수 없는 유형인 never이 됩니다. 오류는 나중에 사용하려고 할 때만 나타납니다.

type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };

// error TS2322: Type 'number' is not assignable to type 'never'.

이후의 오류는 실제 실수와는 거리가 멀기 때문에 더 혼란스럽습니다. 이것이 객체 유형을 구축할 때 extends을 선호하는 실용적인 이유 중 하나입니다.

클래스 및 구현

클래스는 implements을 사용하여 모양 일치를 약속할 수 있으며 이는 인터페이스 또는 객체 유형 별칭과 함께 작동합니다.

class Account implements User {
  constructor(public id: number, public name: string) {}
}

오류 없이 컴파일되었습니다. implements은 클래스가 일치하는지만 확인합니다. 속성이나 코드를 추가하지 않습니다. 일치하는 단일 모양이 없기 때문에 클래스는 공용체 유형을 구현할 수 없습니다.

읽기 전용 및 유틸리티 유형

Readonly, Partial, Pick 및 Omit와 같은 유틸리티 유형은 인터페이스와 별칭 모두에서 작동합니다. 결과는 일반적으로 유형 별칭으로 명명되는 새로운 유형입니다.

const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";

// error TS2540: Cannot assign to 'name' because it is a read-only property.

readonly은 컴파일 타임 검사일 뿐이라는 점을 기억하세요. 런타임 시 객체는 일반 JavaScript이며 유형을 우회하는 코드에 의해 여전히 변경될 수 있습니다.

팁: TypeScript가 JavaScript로 컴파일되면 유형이 사라집니다. API 응답 등 외부에서 들어오는 데이터는 유형이 있다는 이유만으로 런타임에 검사되지 않습니다. 예를 들어 스키마 라이브러리를 사용하여 유효성을 검사합니다. 우리의 Fetch API 튜토리얼에서는 API 응답을 안전하게 처리하는 방법을 다룹니다.

컴파일러 성능

TypeScript 팀의 자체 성능 지침에서는 컴파일러가 인터페이스 관계를 더 효율적으로 캐시할 수 있으므로 큰 교차점보다 extends을 사용하는 인터페이스를 선호할 것을 제안합니다. 중소 규모 프로젝트에서는 차이를 거의 느끼지 못할 것입니다. 유형이 깊게 구성된 매우 큰 코드베이스에서는 문제가 될 수 있습니다. 우리는 이것을 직접 측정하지 않았습니다.

어느 것을 사용해야 합니까?

TypeScript 핸드북의 지침에 따라 널리 사용되는 규칙은 다음과 같습니다.

  • 객체 모양, 특히 공용 API와 클래스가 구현하거나 다른 클래스가 확장할 수 있는 모든 항목에는 interface을 사용하세요.
  • 합집합, 튜플, 함수 유형, 매핑 또는 계산 유형에는 type을 사용하세요.
당신은 정의하고 있습니다 사용
사용자 또는 주문과 같은 데이터 모델 인터페이스
허용되는 값 집합 type 조합
고정 길이 쌍 또는 삼중 type 튜플
콜백 서명 유형
라이브러리 유형 확장 interface, 병합을 통해
기존 유형의 변형 type 유틸리티 유형 포함

일부 팀에서는 어디에서나 단순히 type을 사용하는데 그것도 잘 작동합니다. 가장 중요한 것은 일관성이므로 규칙을 선택하고 스타일 가이드에 작성한 다음 Linter가 이를 시행하도록 하세요. typescript-eslint의 ESLint consistent-type-definitions 규칙이 이를 수행할 수 있습니다.

일반적인 실수

  • 끝없는 논쟁. 둘 다 객체에 적합하므로 규칙을 한 번 정하고 다음으로 넘어갑니다.
  • 실수로 인한 병합. 동일한 범위에서 인터페이스 이름을 재사용하면 자동으로 결합됩니다.
  • 충돌하는 교차로. 나중에 혼란스러운 오류가 있는 never 유형을 만듭니다.
  • 런타임에 유형을 신뢰합니다. 해당 유형은 지워지므로 외부 데이터의 유효성을 검사합니다.
  • any를 사용하여 오류를 침묵시킵니다. 검사를 끕니다. unknown을 선호하고 범위를 좁힙니다.

기존 코드베이스에서 이들 사이를 전환

interface을 type로 변경하거나 그 반대로 변경하는 것은 일반적으로 일반 개체 모양에 안전합니다. TypeScript는 이름이 아닌 구조를 기준으로 유형을 비교하기 때문입니다. 전환하기 전에 확인해야 할 몇 가지 사항은 다음과 같습니다.

  • 인터페이스를 유형 별칭으로 변환하면 선언 병합이 제거되므로 동일한 이름을 가진 두 번째 선언은 중복 식별자 오류가 됩니다.
  • 유형 별칭을 인터페이스로 변환하는 것은 객체 모양에만 작동합니다. 공용체, 튜플 및 매핑된 유형은 유형 별칭으로 유지되어야 합니다.
  • 오류 메시지와 편집기 마우스 오버는 유형을 다르게 표시할 수 있으며, 이는 팀원을 놀라게 할 수 있지만 동작을 바꾸지는 않습니다.

하나의 집중된 풀 요청으로 변경하고, 엄격 모드와 테스트 스위트에서 컴파일러를 실행하고, 기능 작업과 혼합하지 않도록 하여 검토자가 다른 변경 사항이 없음을 확인할 수 있도록 하세요.

마무리

인터페이스 대 유형 질문에 대한 답은 차분합니다. 객체에 대해 중복되고 각각 고유한 능력이 있으며 선택보다 일관성이 더 중요합니다. 팀에서 달리 선호하지 않는 한, 다른 모든 것에는 모양과 유형에 대한 인터페이스를 사용하십시오. 차이점을 직접 확인하려면 위의 예를 파일에 복사하고 tsc --noEmit --strict을 실행하세요.

자주 묻는 질문

TypeScript에서 인터페이스와 유형의 차이점은 무엇입니까?

둘 다 물체 모양을 설명할 수 있습니다. 유형만 공용체, 튜플 및 계산 유형의 이름을 지정할 수 있으며 인터페이스만 선언 병합을 지원합니다.

React props에 인터페이스나 유형을 사용해야 합니까?

어느 쪽이든 잘 작동합니다. 많은 팀이 props에 인터페이스를 사용하는 반면 다른 팀은 공용체와의 일관성을 위해 유형을 선호합니다. 하나를 선택하여 프로젝트 전체에 적용하세요.

유형이 인터페이스를 확장할 수 있나요?

예, Admin = User & {Permissions: string[] } 유형과 같은 교차를 사용하면 됩니다. 인터페이스는 객체 유형 별칭을 확장할 수도 있습니다.

인터페이스가 유형보다 빠르나요?

TypeScript의 성능 지침은 대규모 코드베이스의 대규모 교차점에 대한 인터페이스 확장을 선호합니다. 일반적인 프로젝트에서는 차이가 눈에 띄지 않을 것입니다.

선언 병합이란 무엇입니까?

동일한 범위에서 동일한 이름을 가진 두 개의 인터페이스가 하나로 결합되는 경우입니다. 유형 별칭은 병합할 수 없으며 이름이 중복되면 오류가 발생합니다.

계속 학습하세요

Stack Quora와 작업

테스트, WordPress 또는 SEO에 도움이 필요하십니까?

우리는 팀이 더 나은 소프트웨어를 출시하고 사이트를 성장시킬 수 있도록 돕습니다. 현재 진행 중인 작업을 알려주시면 명확한 다음 단계를 제안해 드리겠습니다.

계속 읽으세요

추가 가이드 테스트, AI 및 최신 개발

이 기사가 마음에 드셨나요? 주제를 선택하고 최신 튜토리얼로 바로 이동하세요.

인공지능

AI 환각 설명: 언어 모델이 사물을 구성하는 이유와 이를 줄이는 방법

AI 환각은 그것이 무엇인지, 언어 모델이 환각을 생성하는 이유, 일반적인 유형, 접지 및 프롬프트를 통해 환각을 줄이는 방법, 그리고 우리가 실행한 간단한 점검에 대해 설명했습니다.

기사 읽기 →
DevOps

환경 변수 설명: .env 파일, 비밀 및 구성이 올바르게 완료되었습니다.

우리가 실행한 Python 예제를 통해 설명된 환경 변수: 읽기 및 설정, 모든 값이 문자열인 이유, .env 파일, 프로세스 상속, 비밀 및 유효성 검사.

기사 읽기 →
소프트웨어 개발

API 속도 제한 설명: 알고리즘, 헤더 및 429 오류 처리

우리가 실행한 시뮬레이션을 통해 설명된 API 속도 제한: 토큰 버킷, 고정 창, 슬라이딩 창, 429 응답, 속도 제한 헤더, 백오프를 사용한 재시도 및 테스트 제한.

기사 읽기 →
테스트 자동화

Python에서 조롱: unittest.mock, 패치 및 테스트를 거짓말로 만드는 실수

Unittest.mock을 사용하여 Python에서 모의, 테스트됨: 모의 및 return_value, 부작용, 이름이 사용되는 패치, 사양 및 자동 사양, 모의 날짜 및 일반적인 실수.

기사 읽기 →
소프트웨어 테스트

코드 커버리지 설명: 회선 커버리지와 분기 커버리지 및 100%의 실제 의미

실제 Coverage.py 실행을 통해 설명되는 코드 적용 범위: 라인, 분기 및 조건 적용 범위, 보고서 읽기, 100% 적용 범위가 여전히 버그를 놓칠 수 있는 이유 및 합리적인 대상.

기사 읽기 →
데이터베이스 및 SQL

SQL의 DELETE vs TRUNCATE vs DROP: 차이점, 롤백 및 사용 시기

테스트된 SQL을 통해 DELETE 대 TRUNCATE 대 DROP에 대해 설명합니다. 각각의 제거, 롤백, 트리거, ID 재설정, 속도 및 PostgreSQL, MySQL, SQL Server 및 SQLite의 차이점.

기사 읽기 →
데이터베이스 및 SQL

SQL 뷰 설명: 예제를 통해 함정 생성, 사용 및 방지

우리가 실행한 쿼리로 설명된 SQL 뷰: 뷰 생성, 실시간 결과, 열 숨기기, 성능, 업데이트 가능하고 구체화된 뷰, 손상된 종속성 및 모범 사례.

기사 읽기 →
소프트웨어 개발

TypeScript 인터페이스와 유형: 실제 차이점, 컴파일러로 테스트

TypeScript 인터페이스 대 유형, 실제 컴파일러로 확인: 둘 다 할 수 있는 것, 하나만 할 수 있는 것, 선언 병합, 확장 대 교차점 및 따라야 할 간단한 규칙.

기사 읽기 →
소프트웨어 개발

Python *args 및 **kwargs 설명: 명확한 예가 포함된 유연한 함수

Python *args 및 **kwargs는 우리가 실행한 코드(패킹 및 언패킹, 매개변수 순서, 키워드 전용 및 위치 전용 인수, 데코레이터 및 일반적인 오류)를 설명했습니다.

기사 읽기 →
소프트웨어 개발

Python Lambda 함수: 구문, 실제 사용 및 사용하지 말아야 할 경우

Python 람다 함수는 구문, 키를 사용한 정렬, 맵 및 필터, 클로저, 후기 바인딩 트랩, 일반 def가 더 나은 선택인 경우 등 우리가 실행한 코드로 설명되었습니다.

기사 읽기 →
인공지능

AI 에이전트란 ​​무엇입니까? 작동 방식, 간단한 에이전트 루프를 사용하여 실행할 수 있음

AI 에이전트란 무엇입니까? 우리가 실행한 작은 Python 루프, 실제 위험 및 간단한 챗봇이 더 나은 선택일 때 에이전트 루프, 도구, 메모리 및 가드레일에 대한 일반 가이드입니다.

기사 읽기 →
DevOps

모니터링과 관찰 가능성: 로그, 지표, 추적을 통한 실제 차이점

모니터링과 관찰 가능성 설명: 알려진 오류와 알려지지 않은 오류, 로그, 지표 및 추적, 결제 사건 안내, 골든 신호, OpenTelemetry 및 유용한 경고.

기사 읽기 →
DevOps

Docker 볼륨과 바인딩 마운트: 컨테이너 데이터를 올바른 방식으로 유지

Docker 볼륨과 바인드 마운트 설명: 컨테이너 데이터가 사라지는 이유, 명명된 볼륨, 바인드 마운트, tmpfs, Compose 예제, 백업, 권한 및 각 사용 시기.

기사 읽기 →
테스트 자동화

TDD와 BDD: 주요 차이점, 두 가지 방식으로 구축된 하나의 기능 및 각 사용 시기

TDD와 BDD는 두 가지 방식으로 구축된 하나의 할인 기능, 즉 우리가 실행한 코드를 사용한 적록 리팩토링, 주어진 시점 시나리오, 도구, 장단점 및 각 사용 시기를 설명합니다.

기사 읽기 →
소프트웨어 개발

Git merge vs rebase: 차이점, 예제, 언제 무엇을 쓸까

Git merge vs rebase를 실제 로그 출력, 비교표, rebase의 황금률, 안전한 팀 워크플로로 설명합니다.

기사 읽기 →
소프트웨어 개발

파이썬 리스트 컴프리헨션: 문법, 예제, 피해야 할 때

파이썬 리스트 컴프리헨션 설명: 문법, 필터링, if-else, 중첩 반복문, 실제 측정 시간, 그리고 일반 for 반복문이 더 나은 경우.

기사 읽기 →
데이터베이스 및 SQL

예제로 쉽게 배우는 SQL JOIN (Inner, Left, Right, Full)

샘플 테이블과 쿼리로 SQL JOIN을 단계별로 이해하세요. INNER, LEFT, RIGHT, FULL, CROSS, SELF와 흔한 실수, 성능 팁까지 정리했습니다.

기사 읽기 →