제네릭 기반 클래스 필드 유형

Simon

다음 코드 ( 놀이터 )를 상상해보십시오 .

type AvailableTypes = {
    'array': Array<any>;
    'string': string;
    'object': object;
}

class Wrapper<T extends keyof AvailableTypes> {

    // Is either array, string or object
    private readonly type: T;

    // ERROR: Property 'value' has no initializer and is not definitely assigned in the constructor.
    private readonly value: AvailableTypes[T];

    constructor(type: T) {
        this.type = type;

        /**
         * ERROR:
         *  TS2322: Type 'never[]' is not assignable to type 'AvailableTypes[T]'.   
         *  Type 'never[]' is not assignable to type 'never'.
         */
        switch (type) {
            case 'array':
                this.value = [];
                break;
            case 'string':
                this.value = '';
                break;
            case 'object':
                this.value = {};
                break;
        }
    }
}

두 가지 주요 오류가 있습니다.

TS2322 : 'never []'유형은 'AvailableTypes [T]'유형에 할당 할 수 없습니다.
'never []'유형은 'never'유형에 할당 할 수 없습니다.

해도 AvailableTypes[T]종류의 일에 항상 결의는 선언 AvailableTypes과 함께, T그것의 주요 인.

... 그리고

속성 'value'에는 이니셜 라이저가 없으며 생성자에 확실히 할당되지 않았습니다.

하지만 type필수 및 필요하다 할 수 중 하나 string, array또는object.

내가 여기서 무엇을 놓치고 있습니까?

가능한 관련 SO 스레드 :

최신 정보

( @jcalz 답변 업데이트 ) 속성 value에 따라 유형 검사가 가능해야합니다 type.

// In the Wrapper class, should work since value can only be an array if type is 'array':
public pushValue(val: unknown) {
    if (this.type === 'array') {
        this.value.push(val);
    }
}

운동장

jcalz

근본적인 문제는 제네릭 형식 매개 변수가 제어 흐름 분석을 통해 좁혀지지 않는다는 것입니다. 이는 TypeScript에서 상당히 오랫동안 열려있는 문제입니다 . 자세한 내용 microsoft / TypeScript # 13995 를 참조하세요.

당신이 검사 할 때 typeA의 switch/의 case문, 컴파일러는 유형 좁힐 수 있습니다 type리터럴 형식으로 변수를 "array"하지만 좁은하지 않는 형식 매개 변수 T 에를 "array". 따라서 any[]유형에 값을 할당하는 것이 안전한지 확인할 수 없습니다 AvailableTypes[T]. 컴파일러는 현재 수행하지 않는 분석을 수행해야합니다. 예를 들어 "좋아요, if 유형에서 type === "array"추론 한 다음이 블록 내에서로 범위 좁힐 수 있으므로 유형은 is , aka, 등입니다. 할당하는 것이 안전 합니다. " 그러나 이것은 일어나지 않습니다.TtypecaseT"array"this.valueAvailableTypes["array"]any[][]

동일한 문제로 인해 " value확실히 할당되지 않음"오류가 발생합니다. 컴파일러는 여기에서 제어 흐름 분석을 수행하지 않기 때문에 switch/ case가 모든 가능성을 소진 하는 것을 볼 수있는 수단 T이 없습니다.


여기서 가장 쉬운 해결 방법은 유형 어설 션 을 사용 하여 컴파일러에게 확인할 수 없기 때문에 수행중인 작업을 알고 있음을 알리는 것입니다.

완전성 문제를 처리하기 위해 다음 default과 같이 던지는 케이스를 만들 수 있습니다 .

class Wrapper<T extends keyof AvailableTypes> {

    private readonly type: T;
    private readonly value: AvailableTypes[T];
    constructor(type: T) {
        this.type = type;

        switch (type) {
            case 'array':
                this.value = [] as AvailableTypes[T]; // assert
                break;
            case 'string':
                this.value = '' as AvailableTypes[T]; // assert
                break;
            case 'object':
                this.value = {} as AvailableTypes[T]; // assert
                break;
            default:
                throw new Error("HOW DID THIS HAPPEN"); // exhaustive
        }
    }
}

또는 type에서 T확장 할 수 있습니다. keyof AvailableTypes그러면 컴파일러가 케이스가 완전하다는 것을 이해하는 데 필요한 제어 흐름 분석을 수행 할 수 있습니다 .

class Wrapper<T extends keyof AvailableTypes> {
    private readonly type: T;
    private readonly value: AvailableTypes[T];
    constructor(type: T) {
        this.type = type;
        const _type: keyof AvailableTypes = type; // widen to concrete type
        switch (_type) {
            case 'array':
                this.value = [] as AvailableTypes[T]; // assert
                break;
            case 'string':
                this.value = '' as AvailableTypes[T]; // assert
                break;
            case 'object':
                this.value = {} as AvailableTypes[T]; // assert
                break;
        }
    }
}

(다른 해결 방법 건전성 변화를 구현 사람 코멘트에서 언급은 ) 당신이 값이있는 경우는 사실을 활용하는 t유형 T, 및 키 k유형을 K extends keyof T다음의 유형 t[k]으로 컴파일러에 의해 보일 것이다 T[K]. 우리가 유효한을 할 수 그래서 만약 AvailableTypes, 우리와 그것에 단지 인덱스 할 수 있습니다 type. 아마도 다음과 같습니다.

class Wrapper<T extends keyof AvailableTypes> {
    private readonly type: T
    private readonly value: AvailableTypes[T];
    constructor(type: T) {
        this.type = type;
        const initValues: AvailableTypes = {
            array: [],
            string: "",
            object: {}
        };
        this.value = initValues[type];
    }
}

이것은 타입 단언과 switch문장 보다 훨씬 더 좋은 방법 이며 부팅하기에 상당히 안전한 방법입니다. 따라서 사용 사례에서 금지하지 않는 한이 솔루션을 사용하겠습니다.


좋아요, 그 중 하나가 도움이되기를 바랍니다. 행운을 빕니다!

코드에 대한 플레이 그라운드 링크


업데이트 : 제네릭 클래스 대신 구별 된 공용체

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

분류에서Dev

클래스 제네릭 속성 메서드 반환 유형 추론

분류에서Dev

Scala 제네릭 : 클래스 유형을 사용하기위한 메서드 필요

분류에서Dev

제네릭 클래스 유형 제약이있는 제네릭 메서드

분류에서Dev

클래스의 다른 제네릭 유형의 인스턴스를 반환하는 단일 메서드

분류에서Dev

정적 메서드에서 실제 제네릭 유형의 클래스 개체 가져 오기

분류에서Dev

정적 메서드에서 실제 제네릭 유형의 클래스 개체 가져 오기

분류에서Dev

제네릭 유형 인스턴스를 가져 오지 않는 제네릭 메서드 내에서 구체적인 클래스 속성 읽기

분류에서Dev

제네릭 클래스의 메서드가 지정된 유형을 반환하지 않습니다. 왜?

분류에서Dev

제네릭 클래스의 메서드가 지정된 유형을 반환하지 않습니다. 왜?

분류에서Dev

단항 형식 메서드 (또는 클래스)에서 muti 형식 제네릭 만들기 및 반환

분류에서Dev

제네릭 유형이 1 개인 클래스에서 제네릭 유형이 2 개인 메서드

분류에서Dev

제네릭 클래스의 제네릭 메서드에 동적 유형의 인스턴스 전달

분류에서Dev

객체가 제네릭 기본 클래스의 인스턴스인지, 제네릭 유형인지 확인

분류에서Dev

제네릭 클래스 유형 확인

분류에서Dev

기본 클래스의 메서드를 사용하여 중첩 클래스에서 제네릭 형식을 반환하는 방법

분류에서Dev

제네릭 반환 유형을 사용하여 메서드에서 구체적인 클래스의 인스턴스를 반환하는 방법

분류에서Dev

제네릭 메서드에 제네릭 클래스 객체 보내기 (Java)

분류에서Dev

제네릭 유형 인수로 유형 'x'를 사용하여 제네릭 클래스를 구현하는 클래스 찾기

분류에서Dev

유형이 기본 클래스를 공유하지 않는 제네릭 유형에 의한 다형성

분류에서Dev

반환 형식을 지정하지 않고 파생 된 제네릭 클래스를 반환하는 메서드

분류에서Dev

Swift : 제네릭 클래스의 제네릭 유형 비교

분류에서Dev

제네릭 형식의 클래스를 반환하는 방법

분류에서Dev

제네릭으로 제네릭 클래스의 클래스 유형을 반환하는 방법은 무엇입니까?

분류에서Dev

EventListener 형식으로 필드 유형 클래스 가져 오기

분류에서Dev

Newexpression을 기반으로 익명 형식의 제네릭 클래스 인스턴스를 반환합니다.

분류에서Dev

제네릭 메서드에서 형식 인수로 사용중인 클래스의 기본 클래스 속성에 액세스 할 수 없습니다.

분류에서Dev

C #의 기본 클래스에서 제네릭 사용 : 기본 클래스의 메서드가 파생 클래스의 형식을 반환하는지 확인하는 방법은 무엇입니까?

분류에서Dev

Typescript : <제네릭>을 사용하여 입력을 기반으로 정확하게 출력 유형을 결정하는 유형 시스템을 만드는 방법

분류에서Dev

생성자가 필요한 제네릭 유형을 상속하는 추상 클래스

Related 관련 기사

  1. 1

    클래스 제네릭 속성 메서드 반환 유형 추론

  2. 2

    Scala 제네릭 : 클래스 유형을 사용하기위한 메서드 필요

  3. 3

    제네릭 클래스 유형 제약이있는 제네릭 메서드

  4. 4

    클래스의 다른 제네릭 유형의 인스턴스를 반환하는 단일 메서드

  5. 5

    정적 메서드에서 실제 제네릭 유형의 클래스 개체 가져 오기

  6. 6

    정적 메서드에서 실제 제네릭 유형의 클래스 개체 가져 오기

  7. 7

    제네릭 유형 인스턴스를 가져 오지 않는 제네릭 메서드 내에서 구체적인 클래스 속성 읽기

  8. 8

    제네릭 클래스의 메서드가 지정된 유형을 반환하지 않습니다. 왜?

  9. 9

    제네릭 클래스의 메서드가 지정된 유형을 반환하지 않습니다. 왜?

  10. 10

    단항 형식 메서드 (또는 클래스)에서 muti 형식 제네릭 만들기 및 반환

  11. 11

    제네릭 유형이 1 개인 클래스에서 제네릭 유형이 2 개인 메서드

  12. 12

    제네릭 클래스의 제네릭 메서드에 동적 유형의 인스턴스 전달

  13. 13

    객체가 제네릭 기본 클래스의 인스턴스인지, 제네릭 유형인지 확인

  14. 14

    제네릭 클래스 유형 확인

  15. 15

    기본 클래스의 메서드를 사용하여 중첩 클래스에서 제네릭 형식을 반환하는 방법

  16. 16

    제네릭 반환 유형을 사용하여 메서드에서 구체적인 클래스의 인스턴스를 반환하는 방법

  17. 17

    제네릭 메서드에 제네릭 클래스 객체 보내기 (Java)

  18. 18

    제네릭 유형 인수로 유형 'x'를 사용하여 제네릭 클래스를 구현하는 클래스 찾기

  19. 19

    유형이 기본 클래스를 공유하지 않는 제네릭 유형에 의한 다형성

  20. 20

    반환 형식을 지정하지 않고 파생 된 제네릭 클래스를 반환하는 메서드

  21. 21

    Swift : 제네릭 클래스의 제네릭 유형 비교

  22. 22

    제네릭 형식의 클래스를 반환하는 방법

  23. 23

    제네릭으로 제네릭 클래스의 클래스 유형을 반환하는 방법은 무엇입니까?

  24. 24

    EventListener 형식으로 필드 유형 클래스 가져 오기

  25. 25

    Newexpression을 기반으로 익명 형식의 제네릭 클래스 인스턴스를 반환합니다.

  26. 26

    제네릭 메서드에서 형식 인수로 사용중인 클래스의 기본 클래스 속성에 액세스 할 수 없습니다.

  27. 27

    C #의 기본 클래스에서 제네릭 사용 : 기본 클래스의 메서드가 파생 클래스의 형식을 반환하는지 확인하는 방법은 무엇입니까?

  28. 28

    Typescript : <제네릭>을 사용하여 입력을 기반으로 정확하게 출력 유형을 결정하는 유형 시스템을 만드는 방법

  29. 29

    생성자가 필요한 제네릭 유형을 상속하는 추상 클래스

뜨겁다태그

보관