[英]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,
};
}
即使您了解Pug
的tags
屬性依賴於Dog
的tags
屬性,編譯器tags = {...this.tags}
視為循環,因此無法確定它將是什么類型。 如果需要,您可以顯式注釋Pug
的tags
屬性,例如:
class PugExplicit extends Dog {
tags: { snorted: boolean, barked: boolean } = {
...this.tags,
snorted: true,
};
}
但這很麻煩。 另一種方法是斷言tags
初始化器中的this
是Dog
而不是Pug
類型:
class Pug extends Dog {
tags = {
...(this as any as Dog).tags,
snorted: true,
};
}
這具有推斷Pug
的tags
是{snorted: boolean, barked: boolean}
類型的預期效果,而無需您顯式注釋它。 (我不確定為什么我需要通過any
來斷言this as any as Dog
而不是this as Dog
。但是,如果沒有中間類型,編譯器仍然會抱怨循環性。我猜this
-ness 通過一個斷言持續存在但不是兩個?)
您將繼承與組合混合在一起,這就是 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.