타입스크립트
GitLab 역사
타입스크립트는 GitLab에서 수년간 검토되고, 논의되며, 홍보되고, 거부되었습니다. 일반적인 결론은 비용이 이점을 초과하므로 타입스크립트를 메인 프로젝트에 통합할 수 없다는 것입니다.
- 메인 프로젝트에는 강력하게 타입이 지정되지 않은 많은 기존 코드가 있습니다.
- 메인 프로젝트의 주요 기여자들은 모두 타입스크립트에 익숙하지 않습니다.
메인 프로젝트 외에도, 타입스크립트는 일부 위성 프로젝트에서 유익하게 사용되고 있습니다.
타입스크립트를 사용하는 프로젝트
다음 GitLab 프로젝트는 타입스크립트를 사용합니다:
gitlab-web-ide
gitlab-vscode-extension
gitlab-language-server-for-code-suggestions
gitlab-org/cluster-integration/javascript-client
권장 사항
ESLint 및 타입스크립트 설정
새로운 타입스크립트 프로젝트를 설정할 때, ESLint 및 타입스크립트의 엄격한 타입 안전성 규칙을 구성하세요. 이렇게 하면 프로젝트가 가능한 한 타입 안전을 유지할 수 있습니다.
GitLab 워크플로우 확장 프로젝트는 타입스크립트 프로젝트의 보일러플레이트 및 구성에 대한 좋은 모델입니다. 거기서 tsconfig.json
및 .eslintrc.json
을 복사하는 것을 고려하세요.
tsconfig.json
에 대해:
-
"strict": true
를 사용하세요.
이는 프로젝트에서 가장 강력한 타입 검사 기능을 강제하고 타입 안전성을 오버라이드하는 것을 금지합니다. -
"skipLibCheck": true
를 사용하세요.
이는 모든.d.ts
파일이 아닌.d.ts
파일 참조만 검사하여 컴파일 시간을 개선합니다.
.eslintrc.json
(또는 .eslintrc.js
)에서:
-
타입스크립트 전용 파싱 및 린트 설정이
**/*.ts
파일에 대한overrides
에 배치되어 있는지 확인하세요.
이렇게 하면 일반.js
파일의 린팅은 타입스크립트 전용 규칙의 영향을 받지 않습니다. -
다음에서 확장하세요:
plugin:@typescript-eslint/recommended
여기에는 다음과 같은 매우 합리적인 기본 설정이 포함되어 있습니다:
any
피하기
무슨 일을 하든 any
를 피하세요. 이는 프로젝트의 린터에 이미 구성되어야 하지만, 여기서 강조할 가치가 있습니다.
개발자들은 일반적으로 도메인 경계를 넘는 데이터 구조를 처리할 때 any
를 사용하는 경우가 많습니다. 예를 들어, HTTP 응답을 처리하거나 타입이 지정되지 않은 라이브러리와 상호 작용할 때입니다. 처음에는 편리해 보일 수 있지만, 잘 정의된 타입을 선택하거나 unknown
을 사용하고 예측자를 통해 타입 축소를 적용하는 것이 상당한 이점을 제공합니다.
// 나쁜 :(
function handleMessage(data: any) {
console.log("우리는 데이터가 무엇인지 모릅니다. 이로 인해 문제가 발생할 수 있습니다!", data.special.stuff);
}
// 좋은 :)
function handleMessage(data: unknown) {
console.log("가끔은 그것이 알 수 없는 것이 괜찮습니다.", JSON.stringify(data));
}
// 또한 좋은 :)
function isFooMessage(data: unknown): data is { foo: string } {
return typeof data === 'object' && data && 'foo' in data;
}
function handleMessage(data: unknown) {
if (isFooMessage(data)) {
console.log("이제 우리는 foo라는 것을 알고 있습니다. 안전합니다!", data.foo);
}
}
< >
또는 as
로 캐스팅을 피하십시오
가능한 한 < >
또는 as
를 사용한 캐스팅을 피하십시오.
타입 캐스팅은 명시적으로 타입 안전성을 우회합니다. 타입 예측자를 사용하는 것을 고려하십시오.
// 나쁨 :(
function handler(data: unknown) {
console.log((data as StuffContainer).stuff);
}
// 좋음 :)
function hasStuff(data: unknown): data is StuffContainer {
if (data && typeof data === 'object') {
return 'stuff' in data;
}
return false;
}
function handler(data: unknown) {
if (hasStuff(data)) {
// 캐스팅 필요 없음 :)
console.log(data.stuff);
}
throw new Error('데이터에 stuff가 있어야 합니다. 재앙적인 결과가 뒤따를 수 있습니다...');
}
이것이 허용될 수 있는 드문 경우가 있습니다(이 테스트 유틸리티를 고려하십시오). 그러나 99%의 경우 더 나은 방법이 있습니다.
새로운 구조체를 위해 type
보다 interface
를 선호하십시오
새로운 구조체를 정의할 때 새로운 type
별칭을 선언하는 것보다 새로운 interface
를 선언하는 것을 선호하십시오.
인터페이스와 타입 별칭은 많은 교차점이 있지만, 오직 인터페이스만 implements
키워드와 함께 사용할 수 있습니다. 클래스는 type
을 implement
할 수 없고(오직 interface
만 가능), 따라서 type
을 사용하면 구조체의 사용성이 제한됩니다.
// 나쁨 :(
type Fooer = {
foo: () => string;
}
// 좋음 :)
interface Fooer {
foo: () => string;
}
타입스크립트 가이드에서:
경험적으로 말씀드리자면,
type
의 기능을 사용해야 할 때까지는interface
를 사용하십시오.
기존 타입의 별칭을 정의하기 위해 type
을 사용하십시오
기존 타입, 클래스 또는 인터페이스의 별칭을 정의하기 위해 type
을 사용하십시오. 타입스크립트 유틸리티 타입을 사용하여 변환을 제공합니다.
interface Config = {
foo: string;
isBad: boolean;
}
// 나쁨 :(
type PartialConfig = {
foo?: string;
isBad?: boolean;
}
// 좋음 :)
type PartialConfig = Partial<Config>;
추론을 개선하기 위해 유니온 타입을 사용하십시오
// 나쁨 :(
interface Foo { type: string }
interface FooBar extends Foo { bar: string }
interface FooZed extends Foo { zed: string }
const doThing = (foo: Foo) => {
if (foo.type === 'bar') {
// 나쁜 캐스팅 :(
console.log((foo as FooBar).bar);
}
}
// 좋음 :)
interface FooBar { type: 'bar', bar: string }
interface FooZed { type: 'zed', zed: string }
type Foo = FooBar | FooZed;
const doThing = (foo: Foo) => {
if (foo.type === 'bar') {
// 캐스팅 필요 없음 :) - TS는 우리가 이제 FooBar라는 것을 알고 있습니다
console.log(foo.bar);
}
}
미래 계획
- TypeScript 프로젝트에서 재사용할 수 있는 공유 ESLint 구성.