簡體   English   中英

在 TypeScript 中隱式擴展 JavaScript 類屬性

[英]Extending JavaScript class properties in TypeScript implicitly

假設我在 JavaScript 中有這些類,我正在嘗試將它們轉換為 TypeScript:

class Dog {
    tags = {
        barked: true,
    };
}

class Pug extends Dog {
    tags = {
        ...this.tags,
        snorted: true,
    };
}

const pug = new Pug();

但是,TypeScript 中的相同代碼會產生以下錯誤:

error TS7022: 'tags' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

這里的錯誤很清楚。 我在重新定義tags時使用this ,所以 TypeScript 很不高興。

有沒有其他方法可以安撫 TypeScript? 到目前為止,我想到的唯一方法是按照錯誤提示進行操作,並明確鍵入它。 然而,在更大的類型定義中,它會變得重復。

歸根結底,我的類的屬性是定義的鍵/值對的對象。 並且每個子類都擴展了超類的對象並添加了額外的鍵/值。

你幾乎做對了,但你需要傳播super的標簽而不是this

class Pug extends Dog {
    tags = {
        ...super.tags, // <-- change from 'this' to 'super'
        snorted: true,
    };
}

游樂場鏈接

即使了解Pugtags屬性依賴於Dogtags屬性,編譯器tags = {...this.tags}視為循環,因此無法確定它將是什么類型。 如果需要,您可以顯式注釋Pugtags屬性,例如:

class PugExplicit extends Dog {
  tags: { snorted: boolean, barked: boolean } = {
    ...this.tags,
    snorted: true,
  };
}

但這很麻煩。 另一種方法是斷言tags初始化器中的thisDog而不是Pug類型:

class Pug extends Dog {
  tags = {
    ...(this as any as Dog).tags,
    snorted: true,
  };
}

這具有推斷Pugtags{snorted: boolean, barked: boolean}類型的預期效果,而無需您顯式注釋它。 (我不確定為什么我需要通過any來斷言this as any as Dog而不是this as Dog 。但是,如果沒有中間類型,編譯器仍然會抱怨循環性。我猜this -ness 通過一個斷言持續存在但不是兩個?)

Playground 代碼鏈接

您將繼承與組合混合在一起,這就是 Typescript 不喜歡您正在做的事情的原因。

最優雅的方式是做一些像 React 那樣的事情。

每次擴展一個類時,您都將添加的屬性作為泛型傳遞。

出於某種原因,僅在使用構造函數時才有效。 但也許其他人可以給你比這更好的方法。

class Dog<T = {}> {
    tags = {
        barked: true,
    } as {barked: boolean} & T;
}

class Pug extends Dog<{snorted: boolean}> {
    constructor() {
      super();
      this.tags = {
        ...this.tags,
        snorted: true,
      };
    }
}

const pug = new Pug();

打字稿廣場

首先,這是我要使用的簡單解決方案

class DogTags {
    barked = true;
}

class PugTags extends DogTags {
    snorted = true;
}

class Dog {
    tags = new DogTags();
}

class Pug extends Dog {
    tags = new PugTags();
}

說實話,我不是這個解決方案的忠實粉絲。 但它正確地模擬了我想要的原始問題,而沒有使用任何過於“hacky”的東西,例如拋出一堆as any TypeScript 強制的東西。

Jorjon 的回答正確地表明我正在嘗試通過繼承來修復組合范式。 如果可以的話,一個更好的、僅由繼承驅動的答案應該是這樣的:

class Dog {
    tagBarked = true;
}

class Pug extends Dog {
    tagSnorted = true;
}

典型的類繼承會自動繼承這些屬性,因此不需要 hacky 代碼。 但是對於我從 JS 更新到 TS 的大型代碼庫,這個解決方案將不起作用,因為對於Pug類,它總是被稱為pug.tag.snorted ,而且通常也是動態的。 一旦我完成了 TS 轉換,我就可以開始解決上面提到的范式問題。 而是一步一個腳印。

最后,對於任何未來的讀者來說,這里還有一個沒有提到的選項:

class Dog {
    getTags = () => ({
        barked: true,
    });

    tags: ReturnType<this["getTags"]> = this.getTags() as any;
}

class Pug extends Dog {
    getTags = () => ({
        ...super.getTags(),
        snorted: true,
    });
}

tags上的動態顯式鍵入對我來說感覺非常 hacky,所以這就是為什么我沒有使用它作為我的解決方案。 另外,我一直在尋找隱式類型。 也許這種方法的見解會對其他開發人員有用。

對於以后看這個的任何人。 似乎問題在於 ts 類型不能引用自己。

只需將循環引用移出即可讓 ts 知道類型。

type DogTags = Dog["tags"]
type PugTags = DogTags & Pug["pugTags"]

class Dog {
    tags = {
        barked: true,
    }
}

class Pug extends Dog {

    pugTags = { snorted: true }

    tags: PugTags = {
        ...this.tags,
        ...this.pugTags
    }

    
}

const dog = new Dog();
console.log('dog.tags', dog.tags)

const pug = new Pug();
console.log('pug.tags', pug.tags)

這是示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM