简体   繁体   English

打字稿:声明对象上的所有属性必须是相同的类型

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

In Typescript you can declare that all elements in an array are of the same type like this:在 Typescript 中,您可以声明数组中的所有元素都是相同类型,如下所示:

const theArray: MyInterface[]

Is there anything similar you can do that declares that ALL of an object's property values must be of the same type?有没有类似的事情可以声明一个对象的所有属性值必须是相同的类型? (without specifying every property name) (不指定每个属性名称)

For example, I'm currently doing this:例如,我目前正在这样做:

interface MyInterface {
    name:string;
}

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

...note how I have to specify <MyInterface> for every single property. ...请注意我必须如何为每个属性指定<MyInterface> Is there any kind of shortcut for this?有什么捷径吗? ie I'm imagining something like this...即我正在想象这样的事情......

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

MyInterface{} is the part that's invalid code and I'm looking for a way to do with less redundancy, and optionally the extra strictness that prevents any other properties being adding to the object of a differing type. MyInterface{}是无效代码的部分,我正在寻找一种减少冗余的方法,以及可选的额外严格性,以防止将任何其他属性添加到不同类型的对象中。

Solution 1: Indexable type方案一: 可转位型

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" },
}

The downside here is that you'd be able to access any property off of allTheThings without any error:这里的缺点是您可以访问allTheThings的任何属性而不会出现任何错误:

allTheThings.nonexistent // type is Thing

This can be made safer by defining ThingMap as [thingName: string]: Thing | void这可以通过将ThingMap定义为[thingName: string]: Thing | void来更安全: [thingName: string]: Thing | void , but that would require null checks all over the place, even if you were accessing a property you know is there. [thingName: string]: Thing | void ,但这需要在整个地方进行空检查,即使您正在访问您知道存在的属性。

Solution 2: Generics with a no-op function解决方案 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

The createThings function has a generic M , and M can be anything, as long as all of the values are Thing , then it returns M . createThings函数有一个通用的M ,并且M可以是任何东西,只要所有的值都是Thing ,那么它就会返回M When you pass in an object, it'll validate the object against the type after the extends , while returning the same shape of what you passed in.当您传入一个对象时,它将根据extends之后的类型验证该对象,同时返回与您传入的相同的形状。

This is the "smartest" solution, but uses a somewhat clever-looking hack to actually get it working.这是“最聪明”的解决方案,但使用了一个看起来有点聪明的 hack 来真正让它工作。 Regardless, until TS adds a better pattern to support cases like this, this would be my preferred route.无论如何,在 TS 添加更好的模式来支持这样的案例之前,这将是我的首选路线。

Some alternatives for single level (flat) objects:单层(平面)对象的一些替代方案:

Alternative 1 ( indexable type ):备选方案 1( 可转位类型):

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

Alternative 2 ( Record ):备选方案 2( 记录):

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

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

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

Use generic and specify which properties type do you want.使用泛型并指定您想要的属性类型。

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

Note: if the list of property names has to be limited, keys have to be specified explicitly:注意:如果必须限制属性名称列表,则必须明确指定键:

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...

Approach Generics with a no-op function can be extended to have a generic function accepting a type of required values, which itself returns no-op function.方法具有无操作函数的泛型可以扩展为具有接受某种类型所需值的泛型函数,该函数本身返回无操作函数。 This way it won't be required to create new function for each type这样就不需要为每种类型创建新功能

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

To understand what happens here below is alternative declaration of typedRecord function from above.要了解下面发生的情况,请参阅上面的typedRecord函数的替代声明。 typedRecord function accepts type parameter TValue for the property type of the record and returns another function which will be used to validate structure of the type T passed to it (TS compiler will infer T from invocation) typedRecord函数接受记录的属性类型的类型参数TValue并返回另一个函数,该函数将用于验证传递给它的类型T的结构(TS 编译器将从调用推断T

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

This covers all requirements这涵盖了所有要求

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

this one working perfectly这个工作完美

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 typescript 中声明 object 中的所有属性相同类型? - declare all property in object the same type in typescript? Typescript,声明变量类型必须是对象的键 - Typescript, declare a variable type must be key of an object 为任意对象属性声明 Typescript 类型 - Declare Typescript type for arbitrary object properties 如何在 TypeScript 中声明一个数组,它可以是多种类型之一,递归地,必须都是相同的类型 - How to declare an array in TypeScript that can be one of many types, recursively, that must be all the same type 将对象属性约束为具有在 TypeScript 中的键必须相同的属性的对象 - Constrain objec properties to be object with properties whose keys must be the same in TypeScript 打字稿 - 声明类型的对象 - Typescript - declare object of type 如果动态添加这些属性,是否可以在 TypeScript 中声明对象中属性的类型? - Is it possible in TypeScript to declare type of the properties in an object if those are dynamically added? 如何仅使用TypeScript声明属性为字符串类型的对象? - How to declare an object whose properties are of type string only using TypeScript? 你能声明一个允许在打字稿中使用未知属性的对象文字类型吗? - Can you declare a object literal type that allows unknown properties in typescript? 打字稿,检查对象是否具有所有类型属性 - Typescript, check if object have all type properties
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM