Typescript:声明对象上的所有属性必须属于同一类型

LaV*_*che 6 types interface type-hinting type-safety typescript

在Typescript中,您可以声明数组中的所有元素都是这样的类型:

const theArray: MyInterface[]
Run Code Online (Sandbox Code Playgroud)

是否有类似的东西可以声明所有对象的属性值必须是同一类型?(没有指定每个属性名称)

例如,我目前正在这样做:

interface MyInterface {
    name:string;
}

const allTheThingsCurrently = {
    first: <MyInterface>{name: 'first thing name' },
    second: <MyInterface>{name: 'second thing name' },
    third: <MyInterface>{name: 'third thing name' },
    //...
};
Run Code Online (Sandbox Code Playgroud)

...注意我必须<MyInterface>为每个属性指定.这有什么捷径吗?即我想象这样的事情......

const allTheThingsWanted:MyInterface{} = {
    first: {name: 'first thing name' },
    second: {name: 'second thing name' },
    third: {name: 'third thing name' },
    //...
};
Run Code Online (Sandbox Code Playgroud)

MyInterface{} 是无效代码的部分,我正在寻找一种更少冗余的方法,以及可选的额外严格性,以防止任何其他属性添加到不同类型的对象.

kin*_*aro 14

解决方案1:可转换类型

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" },
}
Run Code Online (Sandbox Code Playgroud)

这里的缺点是你可以访问任何属性而allTheThings没有任何错误:

allTheThings.nonexistent // type is Thing
Run Code Online (Sandbox Code Playgroud)

通过定义ThingMapas 可以使这更安全[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
Run Code Online (Sandbox Code Playgroud)

createThings函数具有泛型M,并且M可以是任何东西,只要所有值都是Thing,然后它返回M.传入一个对象时,它会根据该类型对该类型进行验证extends,同时返回与传入的内容相同的形状.

这是"最聪明"的解决方案,但使用一个看起来有点聪明的黑客来实际让它工作.无论如何,在TS添加更好的模式来支持这样的情况之前,这将是我的首选路线.

  • 当然,它有效,我只是希望有一个比无用的函数调用更透明的选项,但是哦 (3认同)

Fel*_*ger 6

单层(平面)对象的一些替代方案:

备选方案 1(可索引类型):

const exampleObj: { [k: string]: string } = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}
Run Code Online (Sandbox Code Playgroud)

备选方案 2(记录):

const exampleObj: Record<string, string> = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}
Run Code Online (Sandbox Code Playgroud)

备选方案 3(记录/联合):

const exampleObj: Record<'first' | 'second' | 'third', string> = {
  first: 'premier',
  second: 'deuxieme',
  third: 'troisieme',
}
Run Code Online (Sandbox Code Playgroud)


am0*_*0wa 5

使用泛型并指定您想要的属性类型。

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
Run Code Online (Sandbox Code Playgroud)

注意:如果必须限制属性名称列表,则必须明确指定键:

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...
Run Code Online (Sandbox Code Playgroud)