英文:
How do you restrict keys on a Record type?
问题
如何限制记录/对象上的键?
在下面的示例中,如果您传递给函数的对象包含在我的 DIMENSIONS
对象中存在的键或值,我希望能看到一个 TypeScript 错误。
我可以在值上生成一个错误,但我尝试过的所有方法都无法在键上生成错误。
const DIMENSIONS = {
userType: 1,
moduleVersion: 2
} as const;
type DimensionKeys = keyof typeof DIMENSIONS;
type DimensionValues = typeof DIMENSIONS[DimensionKeys];
type x = typeof DIMENSIONS;
function process<T extends string, V extends number>(yourDimensions: Record<Exclude<T, DimensionKeys>, Exclude<V, DimensionValues>>){
return {...yourDimensions, ...DIMENSIONS};
}
// ----------------------------------------------
// 好的,继续
// ----------------------------------------------
const myCorrectDimensions = {
country: 33
} as const;
const combinedDimensions = process(myCorrectDimensions);
// ----------------------------------------------
// 不好意思,那个值已被保留
// ----------------------------------------------
const myInvalidValueDimensions = {
country: 1
} as const;
const badDimensions = process(myInvalidValueDimensions);
// ----------------------------------------------
// 不好意思,那个键已被保留
// ----------------------------------------------
const myInvalidKeyDimensions = {
userType: 79
} as const;
const badDimensions2 = process(myInvalidKeyDimensions);
英文:
How do you restrict keys on a Record/object?
In the example below, I want to see a ts error if the object you pass to a function contains a key or value present in my DIMENSIONS
object.
I can make an error on a value, but nothing I've tried works on a key.
const DIMENSIONS = {
userType: 1,
moduleVersion: 2
} as const;
type DimensionKeys = keyof typeof DIMENSIONS;
type DimensionValues = typeof DIMENSIONS[DimensionKeys];
type x = typeof DIMENSIONS;
function process<T extends string, V extends number>(yourDimensions: Record<Exclude<T, DimensionKeys>, Exclude<V,DimensionValues>>){
return {...yourDimensions, ...DIMENSIONS};
}
// ----------------------------------------------
// cool, go ahead
// ----------------------------------------------
const myCorrectDimensions = {
country: 33
} as const;
const combinedDimensions = process(myCorrectDimensions);
// ----------------------------------------------
// Na mate, that value is reserved
// ----------------------------------------------
const myInvalidValueDimensions = {
country: 1
} as const;
const badDimensions = process(myInvalidValueDimensions);
// ----------------------------------------------
// Na mate, that key is reserved
// ----------------------------------------------
const myInvalidKeyDimensions = {
userType: 79
} as const;
const badDimensions2 = process(myInvalidKeyDimensions);
答案1
得分: 1
以下是您要翻译的代码部分:
One approach looks like this:
function process<T extends Record<keyof T, number>>(
yourDimensions: { [K in keyof T]:
K extends DimensionKeys ? never :
T[K] extends DimensionValues ? never :
T[K]
}) {
return { ...yourDimensions, ...DIMENSIONS };
Here we have a single objectlike [generic](https://www.typescriptlang.org/docs/handbook/2/generics.html) type parameter `T` corresponding to the type of the function parameter `yourDimensions`. It is [constrained](https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints) to `Record<keyof T, number>`, meaning it can have any keys at all, but its values must be assignable to `number`.
Then, the actual type of `yourDimensions` is the [mapped type](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) `{[K in keyof T]: K extends DimensionKeys ? never : T[K] extends DimensionValues ? never : T[K]}`. TypeScript will infer `T` to be the type of `yourDimensions`, but then check it against that mapped type. For each key `K` from the keys of `T`, the accepted value type `K extends DimensionKeys ? never : T[K] extends DimensionValues ? never : T[K]` is a [conditional type](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) that evaluates to to the input property type `T[K]` if and only if the input property does not use a key of `DimensionKeys` or a value of `DimensionValues`. Otherwise it will be of type `never`, which is going to be an error (there are no values of type `never`).
Let's test it:
const example = process({
a: 3, // okay
b: 2, // error
moduleVersion: 3 // error
})
/* function process<{
a: 3;
b: 2;
moduleVersion: number;
}>(yourDimensions: {
a: 3;
b: never;
moduleVersion: never;
}) */
Here the compiler infers `T` to be something like `{a: 3, b: 2, moduleVersion: number}`, so the type of the `yourDimensions` parameter is `{a: 3, b: never, moduleVersion: never}`. But the input `{a: 3, b: 2, moduleVersion: 3}` doesn't match that type. The `a` property is fine, but the other two cause errors because `2` and `3` are not of type `never`.
[Playground link to code](https://www.typescriptlang.org/play?target=99&jsx=0&ts=5.1.3#code/MYewdgzgLgBAIgSQLIFEByBlBB5TMC8MA3gFAwwCuEApgE4AqAngA7UBcMAjADRkwC2IACYUANtQBqdCAEtwHAEwkAvjACGEGKEhQA3CRJQW1eDP7VIcsAGlqjTYQDWdkADMYR1m-jJ0WXBj6niZwZhay4BJqohTUDh7G3oiomDiYANqh5pbgtvYAukHGMAAeBAle7sl+aYEGrhRgwFBWMMy0IMBxEAA89DDUJVAWQpoAStSgtEI9zoze9NwwYBT8AEZ0AHybABR88xS0WeFWEBxEMOnWMDJgMHML+Wx85NeDw2CjptkRNnaaAH5ltQAG50GDPcjkehXfIDIYjTTHHJgKIxOIwIFgUHgyFQmHWfJ8ZQASmIfFo1CghzuFwAdAyDkcwiiIEsGXTqqkAjBlPplAYAPSCmAAWnFEslUulMtlctFJGFUOVKq0IBAoiWAHMQOoABbUNRCRUi+Vm80WkjaaACRgAYRAtEpzWRv3ipHIoEaUFojA4AGZ-Sp1JprXoreAbaB1rdqEJXadyu1Ot0dvx7Y7nVAE5GSfoTWKLUXixKC6qYGg1AI1MMllA9TWYCDorEbppKTRaGDjUqS33LWHbQgwM3RDIhGjYjnIOUPWrvb6OJxgxo1Tp9IO1kbp-Fk10IBA04xh6Px5PqDu80LTf3b7Ky6rK9Xax4G7A5m2YB26N2C3f-1KEY6EOI7ROOeQ7rOfBUHQTCsBwADsACcK6hpG4abtuLJugoSYdPuh7pieYFCBB2GnFeBiDoMaj8Mw4h4SmB47HOagBrw5BrIoHECMIYiSNIVgBioJJCgAVDADRNC04BtPh3Q9HO5BsTA-r6MqXEwAo6lQoIIjiFItC-BwKzrHQ-K7EyO7nC86gBjpnEmTitAObx+kCUZQnAmCLkiTZyoqWptmadiPmuXp-GGcZ3nmcGABk5LKpSRrgKIjCUJ2cHsFwrnJUIqXpRFBmCfIWn8jAYmCkAA)
英文:
One approach looks like this:
function process<T extends Record<keyof T, number>>(
yourDimensions: { [K in keyof T]:
K extends DimensionKeys ? never :
T[K] extends DimensionValues ? never :
T[K]
}) {
return { ...yourDimensions, ...DIMENSIONS };
Here we have a single objectlike generic type parameter T
corresponding to the type of the function parameter yourDimensions
. It is constrained to Record<keyof T, number>
, meaning it can have any keys at all, but its values must be assignable to number
.
Then, the actual type of yourDimensions
is the mapped type {[K in keyof T]: K extends DimensionKeys ? never : T[K] extends DimensionValues ? never : T[K]}
. TypeScript will infer T
to be the type of yourDimensions
, but then check it against that mapped type. For each key K
from the keys of T
, the accepted value type K extends DimensionKeys ? never : T[K] extends DimensionValues ? never : T[K]
is a conditional type that evaluates to to the input property type T[K]
if and only if the input property does not use a key of DimensionKeys
or a value of DimensionValues
. Otherwise it will be of type never
, which is going to be an error (there are no values of type never
).
Let's test it:
const example = process({
a: 3, // okay
b: 2, // error
moduleVersion: 3 // error
})
/* function process<{
a: 3;
b: 2;
moduleVersion: number;
}>(yourDimensions: {
a: 3;
b: never;
moduleVersion: never;
}) */
Here the compiler infers T
to be something like {a: 3, b: 2, moduleVersion: number}
, so the type of the yourDimensions
parameter is {a: 3, b: never, moduleVersion: never}
. But the input {a: 3, b: 2, moduleVersion: 3}
doesn't match that type. The a
property is fine, but the other two cause errors because 2
and 3
are not of type never
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论