我不知道如何在Typescript中打字。
我有一個正在移植的Javascript類,在此簡化:
我有一個正在移植的Javascript類。
class A {
constructor ({
a = 'hi',
b = 5,
...其余
} = {}) {
this.a = a
this.b = b
this.extra = rest
}
建構式接收一個帶有一些默認值的選項物件,并將其余部分收集到一個物件中。那個rest params物件應該是扁平的,沒有嵌套的物件或陣列,并且需要是JSON可序列化的,并且是完全可選的。但如果它是存在的話,我想為呼叫者提供一個機會,讓它成為良好的型別。所以我定義了幾個型別:
type KnownStuff = {
a: string
b: 數字
}
型別 FlatJSONCompatibleObject = {
[k: string]: string | number | boolean | null
此外,我希望能夠為其余部分指定一些額外的引數:
我希望能夠為其余部分指定一些額外的引數。
type Test = {
c: boolean
d: 'a' | 'b'
所以我試著做了以下的作業:
class A < T extends FlatJSONCompatibleObject = {}> {
a: string
b: 數字
extra: T
構造器({
a = 'hi',
b = 5,
...其余
}: Partial<KnownStuff> & T = {}) {
this.a = a
this.b = b
this.extra = rest // failed
}
}
這是不可能的,因為泛型并不受限制。所以接下來:
class Foo < T extends Omit<FlatJSONCompatibleObject, keyof KnownStuff> > {
a: string
b: 數字
extra: T
構造器({
a = 'hi',
b = 5,
...其余
}: Partial<KnownStuff> & T = {}) {
this.a = a
this.b = b
this.extra = rest
}
但這無法編譯,我無法獲得正確的通用約束。
我最接近的方法是這樣的:
class Bar< T extends Omit<FlatJSONCompatibleObject, keyof KnownStuff> > {
a: string
b: 數字
extra: Omit<FlatJSONCompatibleObject, keyof KnownStuff>
constructor({
a = 'hi',
b = 5,
}: Partial<KnownStuff> = {}, rest: Partial<T> = {}) {
this.a = a
this.b = b
this.extra = rest
}
這可以編譯,但改變了簽名,我真的想避免這樣做,因為這是作業需要,而且已經被其他多個團隊廣泛使用。這也是錯誤的:
const c = new Bar<Test>()
這應該是失敗的,因為引數上的屬性不是可選的,但我不得不使用Partial<T>來能夠分配默認的空物件。我懷疑我在這棵完全錯誤的樹上吠叫,這就是為什么我提出這個問題。
因此,問題的約束條件是:
- 類需要一個帶有一些已知屬性的選項物件。
- 這些已知的屬性應該都有默認值,
new Whatever()應該可以。 - 類可以也接受物件中的一些額外屬性。
- 如果存在額外的東西,必須是JSON兼容的,并且是平面的(沒有嵌套的物件或陣列)。
- 該類的用戶應該能夠傳遞一個型別引數,這樣額外的東西就有了一個明確定義的和編譯器強制的型別,也就是說,
new Whatever<SomeTypeWithRequiredProperties>()不應該作業。 - 我真的不想改變(從Javascript的角度)建構式的簽名,因為這已經被廣泛使用了。
我怎樣才能打出這樣的字?
Playground if it helps和一個(不正確,但希望能給出要點)測驗線束:
//class test harness。
function test<T extends FlatJSONCompatibleObject = {}> (C: {new <U extends FlatJSONCompatibleObject = {}> (opts? : Partial<KnownStuff> & U)。) LP<T>}) {
//應該作業。
const t1 = new C() 。
t1.extras //應該是'{}'。
const t2 = new C<{c: boolean}>({c: true}) 。
t2.extras.c // should be true.
//應該是編譯錯誤。
const t3 = new C<{c: boolean}>()。
const t4 = new C<{c: boolean}>({d: 4}) 。
uj5u.com熱心網友回復:
我將此作為答案發布(而不是對問題的編輯),因為它至少部分地回答了問題,但我不會接受它,因為a. 我并不完全理解它為什么有效,b. 我只能讓它對函式起作用,而不是對類的構造器起作用。由于 T.J. Crowder 在評論中的幫助,我偶然發現了這個部分解決方案。
如果我們這樣定義一個介面:
type LP<T extends FlatJSONCompatibleObject> = {
a: string
b: 數字
extras: T
然后像這樣一個多載函式:
function bar()。LP<{}>
function bar<T extends FlatJSONCompatibleObject> (opts: Partial<KnownStuff> & T)。) LP<T>
function bar(opts? : Partial<KnownStuff> )。LP<{}>
{
const {
a = 'hi'/span>,
b = 5,
...額外
} = opts || {};
if (opts) {
return {
a,
b,
額外的
}
} else {
return {
a,
b,
extras: {}.
}
}
}
const bar1 = bar() 。
bar1.extras // {}
const bar2 = bar<{c: boolean}>({c: true)
bar2.extras.c // boolean.
const bar3 = bar<{c: boolean}>() //編譯錯誤。
const bar4 = bar<{c: boolean}>({d: boolean}) //編譯錯誤。
這可以作業,但我不能讓它對一個類起作用:
這可以作業。
class Foo < T extends FlatJSONCompatibleObject> {
public a: string
public b: 數字
// public extras: T | {}
public extras: T
constructor()
constructor(opts: Partial<KnownStuff> & T)
constructor(opts? : Partial<KnownStuff>)
{
const {
a = 'hi'/span>,
b = 5,
...額外
} = opts || {};
this.a = a
this.b = b
if (opts) {
this.extras = extras //錯誤
} else {
this.extras = {}。//錯誤。
}
}
}
盡管我可以通過讓額外的東西成為一個明確的第二引數來接近它:
class Bar< T extends FlatJSONCompatibleObject> {
public a: string
public b: 數字
public extras: T
constructor()
constructor(opts: Partial<KnownStuff> , extras: T)
constructor(opts? Partial<KnownStuff>, extras?: never)
{
const {
a = 'hi'/span>,
b = 5..
} = opts || {}
this.a = a
this.b = b
if (extras) {
this.extras = extras
} else {
this.extras = {}。//錯誤!。
}
}
}
我似乎仍然無法獲得正確的多載。
uj5u.com熱心網友回復:
TL;DR
你目前無法用100%型別安全的TypeScript做到這一點。
詳情:
我的 TS 之旅還很早,但我認為這可能是一個案例。我可以做到引數是可選的,但如果你提供了一個具有必要屬性的型別引數,它就不會強制要求有一個引數(下面的#1)。或者我可以做到你沒有默認的引數(盡管有一個相對無害的@ts-ignore我寧愿避免)(下面的#2)。或者在我之前的一個嘗試上稍作改變,就真的接近了(下面的#3),但是extra的型別是{}|x而不是{}。|x,而不僅僅是x。我聯系了Titian Cernicova Dragomir,他確認在今天的TypeScript中你無法做到這一切,并提供了下面的#4,但是和我的#1一樣,當你提供一個型別引數時,它也沒有強制執行這個引數(同時,extra的型別最終是Partial<something>或者Partial<{}>)。
主要的問題是你標記出來的那個問題。你不能將{}分配給一個通用型別的屬性,因為該通用型別的具體型別可能不允許{}。
所以很遺憾,看起來這將是一個選擇最接近的東西的問題。
#1
這是一個不能正確處理new A<something>()的問題:
type KnownStuff = {
a: 字串: a.
b: 數字。
}
type FlatJSONCompatibleObject = {
[k: string] 。string | number | boolean | null >。
}
type Test = {
c: boolean: c.
d: 'a' | 'b'
}
class A < Extra extends FlatJSONCompatibleObject = {}> {
a: 字串。
b:
extra: Extra: extra.
constructor()。
constructor (props: Partial<KnownStuff> & Extra)。)
constructor (props? : any) {
const {a = 'hi', b = 5, ... extra} = props ? {}
this.a = a
this.b = b
this.extra = extra
}
}
const a1 = new A() // All good
a1.extra // type is {}
const a2 = new A<{c: boolean}>({d: "hi"}) // Error as desired.
a2.extra // type is {c: boolean}
const a3 = new A<{c: boolean}>() // No error :-()
a3.extra // Type is {c: boolean}
const a4 = new A<{c: boolean}>({c: true}) // All good
a3.extra // type is {c: boolean}
#2
這是一個不允許向建構式傳遞任何引數的建構式:
type KnownStuff = {
a: 字串: a.
b: 數字。
}
type FlatJSONCompatibleObject = {
[k: string] 。string | number | boolean | null >。
}
type Test = {
c: boolean: c.
d: 'a' | 'b'
}
class A < Extra extends FlatJSONCompatibleObject = {}> {
a: 字串。
b:
extra: Extra: extra.
constructor ({a = 'hi', b = 5, ... extra}。Partial<KnownStuff> & Extra) {
this.a = a
this.b = b
//@ts-ignore.
this.extra = extra
}
}
//不再相關。
// const a1 = new A()
// a1.extra // Type is {}
const a2 = new A<{c: boolean}>({d: "hi"}); // Error as desired.
a2.extra // type is {c: boolean}.
const a3 = new A<{c: boolean}>(); // Error as desired
a3.extra // type is {c: boolean}
const a4 = new A<{c: boolean}>({c: true}); // All good
a3.extra // type is {c: boolean}
#3
這就是你為extra提供了一個尷尬的型別,X | {}而不是僅僅是X:
type KnownStuff = {
a: 字串: a.
b: 數字。
}
type FlatJSONCompatibleObject = {
[k: string] 。string | number | boolean | null >。
}
type Test = {
c: boolean: c.
d: 'a' | 'b'
}
class A < Extra extends FlatJSONCompatibleObject = {}> {
a: 字串。
b:
extra: extra | {}。
constructor (props? Partial<KnownStuff> & Extra) {
if (props) {
const {a = 'hi', b = 5, ... extra} = props
this.a = a
this.b = b
this.extra = extra
} else {
this.a = 'hi'.
this.b = 5.
this.extra = {} 。
}
}
}
const a1 = new A() // All good
a1.extra // type is {}
const a2 = new A<{c: boolean}>({d: "hi"}) // Error as desired.
a2.extra // type is {c: boolean} | {}
const a3 = new A<{c: boolean}>() //錯誤如愿以償。
a3.extra // type is {c: boolean} | {}
const a4 = new A<{c: boolean}>({c: true}) // All good
a3.extra // type is {c: boolean}}. | {}
#4
Titian Cernicova Dragomir的那個有new A<something>()的問題#1有,而extra的型別最后是Partial<{}>或者Partial<something>:
type KnownStuff = {
a: 字串: a.
b: 數字。
}
type FlatJSONCompatibleObject = {
[k: string] 。string | number | boolean | null >。
} | {}
type Test = {
c: boolean: c.
d: 'a' | 'b'
}
class A <T extends FlatJSONCompatibleObject ={}> {
a: 字串。
b:
extra: Partial<T>
constructor({
a = 'hi'/span>,
b = 5,
...其余
}: Partial<KnownStuff> & Partial<T> ={}) {
this.a = a
this.b = b
this.extra = rest as Partial< T>
}
}
const a1 = new A() // All good
a1.extra // type is Partial<{}>
const a2 = new A<{c: boolean}>({d: "hi"}) // Error as desired.
a2.extra //Type is Partial<{c: boolean}>/span>
const a3 = new A<{c: boolean}>() // No error :-()
a3.extra //Type is Partial<{c: boolean}>/span>
const a4 = new A<{c: boolean}>({c: true}) // All good
a3.extra //Type is Partial<{c: boolean}>/span>
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/330168.html
標籤:
