Typescript d.ts 파일에 정의 된 인터페이스 속성 유형 재정의


127

*.d.tsin typescript에 정의 된 인터페이스 속성의 유형을 변경하는 방법이 있습니까?

예 :의 인터페이스 x.d.ts는 다음과 같이 정의됩니다.

interface A {
  property: number;
}

내가 쓰는 typescript 파일에서 변경하고 싶습니다.

interface A {
  property: Object;
}

또는 이것은 작동합니다

interface B extends A {
  property: Object;
}

이 접근 방식이 효과가 있습니까? 내 시스템에서 시도했을 때 작동하지 않았습니다. 가능한지 확인하고 싶습니까?

답변:


56

기존 속성의 유형은 변경할 수 없습니다.

속성을 추가 할 수 있습니다.

interface A {
    newProperty: any;
}

그러나 기존 유형 변경 :

interface A {
    property: any;
}

오류가 발생합니다.

후속 변수 선언은 동일한 유형을 가져야합니다. 변수 'property'는 'number'유형이어야하지만 여기에는 'any'유형이 있습니다.

물론 기존 인터페이스를 확장하는 자체 인터페이스를 가질 수 있습니다. 이 경우 호환되는 유형으로 만 유형을 재정의 할 수 있습니다. 예를 들면 다음과 같습니다.

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

그건 그렇고, 당신은 아마 Object유형으로 사용하지 말고 대신 type을 사용하십시오 any.

유형에 대한 문서에서 다음과any 같이 설명합니다.

any 유형은 기존 JavaScript와 함께 작업하는 강력한 방법으로, 컴파일하는 동안 점차적으로 유형 검사를 옵트 인 및 옵트 아웃 할 수 있습니다. Object가 다른 언어에서와 마찬가지로 비슷한 역할을 할 것으로 예상 할 수 있습니다. 그러나 Object 유형의 변수는 값을 할당 할 수만 있습니다 . 실제로 존재하는 경우에도 임의의 메서드를 호출 할 수 없습니다 .

let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Typescript> = 1.1을 사용하여 인터페이스를 확장하여 메소드 유형을 덮어
jcubic

먼저 덮어 쓰고 싶은 값을 생략하고 재정의 할 수 있습니다. @ZSkycat의 답을 해결하는 것으로 만들 수 있습니까?
zeachco

Java를 '기타 언어'로 지칭하는 것에 대해 반대 투표
wvdz

@wvdz는 내가 반대표에 대해 많이 신경 쓰지 않지만 무슨 말을하고 있습니까? 누구든지 자바를 어디에서 언급 했습니까? "java"에 대한 페이지 검색에는 단 하나의 검색 만 있으며 귀하의 의견에 있습니다.
Nitzan Tomer

내가 좀 심술 궂은 것 같았지만 자바 에서처럼 그냥 말할 수있을 때 "다른 언어"라고 말한 것은 나를 조금 짜증나게했다. 아니면 범용 기본 클래스로 Object를 사용하는 다른 언어가 정말 많습니까? 저는 C #을 알고 있지만 물론 C #은 Java에서 많은 영감을 받았습니다.
wvdz

227

먼저 필드를 필터링 한 다음 결합하는 방법을 사용합니다.

참조 유형에서 속성 제외

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

인터페이스 :

interface A {
    x: string
}

interface B extends Omit<A, 'x'> {
  x: number
}

3
이것을 아는 것이 좋습니다. 그러나 문제는 여전히 기존 항목을 수정하지 않는다는 것입니다.
Freewind

10
이것이 바로 제가 찾던 것입니다. 이것이 내가 타이프 스크립트 extend가 기본적으로 작동 할 것이라고 예상 한 방식 이지만 Omit아쉽게도이 작은 부분이 모든 것을 고친다 🙌
Dawson B

1
인터페이스 확장은 제가 찾던 바로 그 것입니다. 감사합니다!
mhodges

1
참고 :이를 사용하려면 위의 typescript 3.5.3이 필요합니다.
Vixson

92
type ModifiedType = Modify<OriginalType, {
  a: number;
  b: number;
}>

interface ModifiedInterface extends Modify<OriginalType, {
  a: number;
  b: number;
}> {}

ZSkycat의 extends Omit 솔루션에서 영감을 받아 다음과 같은 결과를 얻었습니다.

type Modify<T, R> = Omit<T, keyof R> & R;

// before typescript@3.5
type Modify<T, R> = Pick<T, Exclude<keyof T, keyof R>> & R

예:

interface OriginalInterface {
  a: string;
  b: boolean;
  c: number;
}

type ModifiedType  = Modify<OriginalInterface , {
  a: number;
  b: number;
}>

// ModifiedType = { a: number; b: number; c: number; }

단계별 진행 :

type R0 = Omit<OriginalType, 'a' | 'b'>        // { c: number; }
type R1 = R0 & {a: number, b: number }         // { a: number; b: number; c: number; }

type T0 = Exclude<'a' | 'b' | 'c' , 'a' | 'b'> // 'c'
type T1 = Pick<OriginalType, T0>               // { c: number; }
type T2 = T1 & {a: number, b: number }         // { a: number; b: number; c: number; }

TypeScript 유틸리티 유형


9
이것은 훌륭한 솔루션입니다.
Austin Brunkhorst

1
여기에 멍청하지만 인터페이스에서 유형으로 변경하고 있습니까? 아니면 차이가 없습니까?
Dominic

Niceeeeeeeeee : D
SaMiCoOo

1
@Dominic 좋은 지적, 답변을 업데이트했습니다. 이름이 같은 두 개의 인터페이스를 병합 할 수 있습니다. typescriptlang.org/docs/handbook/…
Qwerty

34

@zSkycat의 대답을 약간 확장하면 두 개체 유형을 받아들이고 첫 번째 구성원을 재정의하는 두 번째 구성원과 병합 된 유형을 반환하는 제네릭을 만들 수 있습니다.

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;

interface A {
    name: string;
    color?: string;
}

// redefine name to be string | number
type B = Merge<A, {
    name: string | number;
    favorite?: boolean;
}>;

let one: A = {
    name: 'asdf',
    color: 'blue'
};

// A can become B because the types are all compatible
let two: B = one;

let three: B = {
    name: 1
};

three.name = 'Bee';
three.favorite = true;
three.color = 'green';

// B cannot become A because the type of name (string | number) isn't compatible
// with A even though the value is a string
// Error: Type {...} is not assignable to type A
let four: A = three;

1
매우 멋지다 :-) 전에 Omit을 사용하여 하나 또는 두 개의 속성으로이 작업을 수행했지만 훨씬 더 멋집니다. :-) 종종 서버 엔티티 유형을 '확장'하고 클라이언트에서 필수 또는 선택 사항으로 변경하고 싶습니다 .
Simon_Weaver

1
이것은 이제 받아 들여진 해결책이 될 것입니다. 인터페이스를 "확장"하는 가장 깨끗한 방법.
manuhortet

10

Omit 인터페이스 확장시 속성 :

interface A {
  a: number;
  b: number;
}

interface B extends Omit<A, 'a'> {
  a: boolean;
}

3

같은 사건을 해결할 가능성을 조사하는 데 하루를 보내는 것이 재미 있어요. 나는 이런 식으로 할 수 없다는 것을 알았다.

// a.ts - module
export interface A {
    x: string | any;
}

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

interface B extends A {
    x: SomeOtherType;
}

원인 모듈은 응용 프로그램에서 사용 가능한 모든 유형에 대해 알지 못할 수 있습니다. 그리고 모든 곳에서 모든 것을 포팅하고 이와 같은 코드를 작성하는 것은 매우 지루합니다.

export interface A {
    x: A | B | C | D ... Million Types Later
}

자동 완성이 잘 작동하도록 나중에 유형을 정의해야합니다.


따라서 약간의 속임수를 쓸 수 있습니다.

// a.ts - module
export interface A {
    x: string;
}

재정의가 필요하지 않은 경우 자동 완성 작업을 허용하는 일부 유형은 기본적으로 그대로 두었습니다.

그때

// b.ts - module
import {A} from './a';

type SomeOtherType = {
  coolStuff: number
}

// @ts-ignore
interface B extends A {
    x: SomeOtherType;
}

여기서 @ts-ignore플래그를 사용하여 어리석은 예외를 비활성화 하여 우리가 뭔가 잘못하고 있다고 말합니다. 그리고 재미있는 것은 모든 것이 예상대로 작동합니다.

제 경우에는 type의 범위 비전을 줄이고 x코드를 더 엄격하게 수행 할 수 있습니다. 예를 들어 100 개의 속성 목록이 있고 어리석은 상황을 피하기 위해 10 개로 줄입니다.


3

속성 유형을 좁히려면 Nitzan의 대답extend 과 같이 간단한 작업이 완벽합니다 .

interface A {
    x: string | number;
}

interface B extends A {
    x: number;
}

유형을 넓히거나 일반적으로 재정의 하려면 Zskycat의 솔루션 을 사용할 수 있습니다 .

interface A {
    x: string
}

export type B = Omit<A, 'x'> & { x: number };

그러나 인터페이스 A가 일반 인터페이스를 확장하는 A경우를 사용할 때의 나머지 속성 의 사용자 지정 유형을 잃게됩니다 Omit.

예 :

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = Omit<A, 'x'> & { x: number };

let b: B = { x: 2, y: "hi" }; // no error on b.y! 

그 이유는 Omit내부적 으로 우리의 경우 Exclude<keyof A, 'x'>일반적인 키만 다루기 때문입니다 string | number. 따라서 유형이 인 추가 속성 B{x: number; }되고 허용 number | string | boolean됩니다.


이를 해결하기 위해 OverrideProps다음과 같은 다른 유틸리티 유형을 생각해 냈습니다 .

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

예:

type OverrideProps<M, N> = { [P in keyof M]: P extends keyof N ? N[P] : M[P] };

interface A extends Record<string | number, number | string | boolean> {
    x: string;
    y: boolean;
}

export type B = OverrideProps<A, { x: number }>;

let b: B = { x: 2, y: "hi" }; // error: b.y should be boolean!

1

다른 사람이이 작업을 수행하기 위해 일반 유틸리티 유형이 필요한 경우 다음 솔루션을 찾았습니다.

/**
 * Returns object T, but with T[K] overridden to type U.
 * @example
 * type MyObject = { a: number, b: string }
 * OverrideProperty<MyObject, "a", string> // returns { a: string, b: string }
 */
export type OverrideProperty<T, K extends keyof T, U> = Omit<T, K> & { [P in keyof Pick<T, K>]: U };

필자의 경우 재정의하는 키가 제네릭 자체이기 때문에 이것이 필요했습니다.

Omit준비 되지 않은 경우 유형에서 속성 제외를 참조하세요 .


1
이것이 제가 찾던 것입니다. 감사합니다. : D : D : D
dwoodwardgb

@dwoodwardgb 다른 사람에게 유용했다 기뻐요 :-)
Toni

0

참고 : 이 답변에서 사용하는 구문이 이전 답변이 작성되었을 때 사용할 수 있는지 확실하지 않지만 이 질문에 언급 된 예제를 해결하는 방법에 대한 더 나은 접근 방식이라고 생각합니다.


이 주제 (인터페이스 속성 덮어 쓰기)와 관련된 몇 가지 문제가 있었는데, 이것이 제가 처리하는 방법입니다.

  1. 먼저 사용하려는 가능한 유형으로 일반 인터페이스를 만듭니다.

에서 볼 수 default있듯이 일반 매개 변수 의 값을 선택할 수도 있습니다.<T extends number | SOME_OBJECT = number>

type SOME_OBJECT = { foo: "bar" }

interface INTERFACE_A <T extends number | SOME_OBJECT = number> {
  property: T;
}
  1. 그런 다음 일반 매개 변수에 값을 전달하여 해당 계약을 기반으로 새 형식을 만들 수 있습니다 (또는 값을 생략하고 기본값 사용).
type A_NUMBER = INTERFACE_A;                   // USES THE default = number TYPE. SAME AS INTERFACE_A<number>
type A_SOME_OBJECT = INTERFACE_A<SOME_OBJECT>  // MAKES { property: SOME_OBJECT }

그리고 이것이 그 결과입니다.

const aNumber: A_NUMBER = {
    property: 111  // THIS EXPECTS A NUMBER
}

const anObject: A_SOME_OBJECT = {
    property: {   // THIS EXPECTS SOME_OBJECT
        foo: "bar"
    }
}

Typescript 놀이터

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.