[英]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{}
是无效代码的部分,我正在寻找一种减少冗余的方法,以及可选的额外严格性,以防止将任何其他属性添加到不同类型的对象中。
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
,但这需要在整个地方进行空检查,即使您正在访问您知道存在的属性。
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:单层(平面)对象的一些替代方案:
const exampleObj: { [k: string]: string } = {
first: 'premier',
second: 'deuxieme',
third: 'troisieme',
}
const exampleObj: Record<string, string> = {
first: 'premier',
second: 'deuxieme',
third: 'troisieme',
}
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.