簡體   English   中英

打字稿:聲明對象上的所有屬性必須是相同的類型

[英]Typescript: declare that ALL properties on an object must be of the same type

在 Typescript 中,您可以聲明數組中的所有元素都是相同類型,如下所示:

const theArray: MyInterface[]

有沒有類似的事情可以聲明一個對象的所有屬性值必須是相同的類型? (不指定每個屬性名稱)

例如,我目前正在這樣做:

interface MyInterface {
    name:string;
}

const allTheThingsCurrently = {
    first: <MyInterface>{name: 'first thing name' },
    second: <MyInterface>{name: 'second thing name' },
    third: <MyInterface>{name: 'third thing name' },
    //...
};

...請注意我必須如何為每個屬性指定<MyInterface> 有什么捷徑嗎? 即我正在想象這樣的事情......

const allTheThingsWanted:MyInterface{} = {
    first: {name: 'first thing name' },
    second: {name: 'second thing name' },
    third: {name: 'third thing name' },
    //...
};

MyInterface{}是無效代碼的部分,我正在尋找一種減少冗余的方法,以及可選的額外嚴格性,以防止將任何其他屬性添加到不同類型的對象中。

方案一: 可轉位型

interface Thing {
  name: string
}

interface ThingMap {
  [thingName: string]: Thing
}

const allTheThings: ThingMap = {
  first: { name: "first thing name" },
  second: { name: "second thing name" },
  third: { name: "third thing name" },
}

這里的缺點是您可以訪問allTheThings的任何屬性而不會出現任何錯誤:

allTheThings.nonexistent // type is Thing

這可以通過將ThingMap定義為[thingName: string]: Thing | void來更安全: [thingName: string]: Thing | void ,但這需要在整個地方進行空檢查,即使您正在訪問您知道存在的屬性。

解決方案 2:具有無操作功能的泛型

const createThings = <M extends ThingMap>(things: M) => things

const allTheThings = createThings({
  first: { name: "first thing name" },
  second: { name: "second thing name" },
  third: { name: "third thing name" },
  fourth: { oops: 'lol!' }, // error here
})

allTheThings.first
allTheThings.nonexistent // comment out "fourth" above, error here

createThings函數有一個通用的M ,並且M可以是任何東西,只要所有的值都是Thing ,那么它就會返回M 當您傳入一個對象時,它將根據extends之后的類型驗證該對象,同時返回與您傳入的相同的形狀。

這是“最聰明”的解決方案,但使用了一個看起來有點聰明的 hack 來真正讓它工作。 無論如何,在 TS 添加更好的模式來支持這樣的案例之前,這將是我的首選路線。

單層(平面)對象的一些替代方案:

備選方案 1( 可轉位類型):

const exampleObj: { [k: string]: string } = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}

備選方案 2( 記錄):

const exampleObj: Record<string, string> = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}

備選方案 3( 記錄/ 聯合):

const exampleObj: Record<'first' | 'second' | 'third', string> = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}

使用泛型並指定您想要的屬性類型。

type SamePropTypeOnly<T> = {
  [P: string]: T;
}

interface MyInterface {
  name: string;
}

const newObj: SamePropTypeOnly<MyInterface> = {
  first: { name: 'first thing name' },
  second: { name: 'second thing name' },
  third: { name: 'third thing name' },
  // forth: 'Blah' // Type 'string' is not assignable to type `MyInterface`
}

newObj.not_there; // undefined - no error

注意:如果必須限制屬性名稱列表,則必須明確指定鍵:

interface MyInterface {
  name: string;
}

type OptionKeys = 'first' | 'second' | 'third';

const newObj: Record<OptionKeys, MyInterface> = {
  first: { name: 'first thing name' },
  second: { name: 'second thing name' },
  third: { name: 'third thing name' },
  // forth: 'Blah' // error
}

newObj.not_there // Property 'not_there' does not exist on type...

方法具有無操作函數的泛型可以擴展為具有接受某種類型所需值的泛型函數,該函數本身返回無操作函數。 這樣就不需要為每種類型創建新功能

export const typedRecord = <TValue>() => <T extends Record<PropertyKey, TValue>>(v: T): T => v;

要了解下面發生的情況,請參閱上面的typedRecord函數的替代聲明。 typedRecord函數接受記錄的屬性類型的類型參數TValue並返回另一個函數,該函數將用於驗證傳遞給它的類型T的結構(TS 編譯器將從調用推斷T

export function typedRecord<TValue>() {
  return function identityFunction<T extends Record<PropertyKey, TValue>>(v: T): T {
    return v;
  };
}

這涵蓋了所有要求

const allTheThings = typedRecord<Thing>()({
  first: { name: "first thing name" },
  second: { name: "second thing name" },
  third: { name: "third thing name" },
  fourth: { oops: "lol!" }, // error here
});

allTheThings.first;
allTheThings.nonexistent; // error here

這個工作完美

const exampleObj: { [k: string]: string } = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}

暫無
暫無

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

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