如何在记录类型上限制键?

huangapple go评论117阅读模式
英文:

How do you restrict keys on a Record type?

问题

如何限制记录/对象上的键?

在下面的示例中,如果您传递给函数的对象包含在我的 DIMENSIONS 对象中存在的键或值,我希望能看到一个 TypeScript 错误。

我可以在值上生成一个错误,但我尝试过的所有方法都无法在键上生成错误。

  1. const DIMENSIONS = {
  2. userType: 1,
  3. moduleVersion: 2
  4. } as const;
  5. type DimensionKeys = keyof typeof DIMENSIONS;
  6. type DimensionValues = typeof DIMENSIONS[DimensionKeys];
  7. type x = typeof DIMENSIONS;
  8. function process<T extends string, V extends number>(yourDimensions: Record<Exclude<T, DimensionKeys>, Exclude<V, DimensionValues>>){
  9. return {...yourDimensions, ...DIMENSIONS};
  10. }
  11. // ----------------------------------------------
  12. // 好的,继续
  13. // ----------------------------------------------
  14. const myCorrectDimensions = {
  15. country: 33
  16. } as const;
  17. const combinedDimensions = process(myCorrectDimensions);
  18. // ----------------------------------------------
  19. // 不好意思,那个值已被保留
  20. // ----------------------------------------------
  21. const myInvalidValueDimensions = {
  22. country: 1
  23. } as const;
  24. const badDimensions = process(myInvalidValueDimensions);
  25. // ----------------------------------------------
  26. // 不好意思,那个键已被保留
  27. // ----------------------------------------------
  28. const myInvalidKeyDimensions = {
  29. userType: 79
  30. } as const;
  31. 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.

  1. const DIMENSIONS = {
  2. userType: 1,
  3. moduleVersion: 2
  4. } as const;
  5. type DimensionKeys = keyof typeof DIMENSIONS;
  6. type DimensionValues = typeof DIMENSIONS[DimensionKeys];
  7. type x = typeof DIMENSIONS;
  8. function process&lt;T extends string, V extends number&gt;(yourDimensions: Record&lt;Exclude&lt;T, DimensionKeys&gt;, Exclude&lt;V,DimensionValues&gt;&gt;){
  9. return {...yourDimensions, ...DIMENSIONS};
  10. }
  11. // ----------------------------------------------
  12. // cool, go ahead
  13. // ----------------------------------------------
  14. const myCorrectDimensions = {
  15. country: 33
  16. } as const;
  17. const combinedDimensions = process(myCorrectDimensions);
  18. // ----------------------------------------------
  19. // Na mate, that value is reserved
  20. // ----------------------------------------------
  21. const myInvalidValueDimensions = {
  22. country: 1
  23. } as const;
  24. const badDimensions = process(myInvalidValueDimensions);
  25. // ----------------------------------------------
  26. // Na mate, that key is reserved
  27. // ----------------------------------------------
  28. const myInvalidKeyDimensions = {
  29. userType: 79
  30. } as const;
  31. const badDimensions2 = process(myInvalidKeyDimensions);

答案1

得分: 1

以下是您要翻译的代码部分:

  1. One approach looks like this:
  2. function process<T extends Record<keyof T, number>>(
  3. yourDimensions: { [K in keyof T]:
  4. K extends DimensionKeys ? never :
  5. T[K] extends DimensionValues ? never :
  6. T[K]
  7. }) {
  8. return { ...yourDimensions, ...DIMENSIONS };
  9. 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`.
  10. 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`).
  11. Let's test it:
  12. const example = process({
  13. a: 3, // okay
  14. b: 2, // error
  15. moduleVersion: 3 // error
  16. })
  17. /* function process<{
  18. a: 3;
  19. b: 2;
  20. moduleVersion: number;
  21. }>(yourDimensions: {
  22. a: 3;
  23. b: never;
  24. moduleVersion: never;
  25. }) */
  26. 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`.
  27. [Playground link to code](https://www.typescriptlang.org/play?target=99&amp;jsx=0&amp;ts=5.1.3#code/MYewdgzgLgBAIgSQLIFEByBlBB5TMC8MA3gFAwwCuEApgE4AqAngA7UBcMAjADRkwC2IACYUANtQBqdCAEtwHAEwkAvjACGEGKEhQA3CRJQW1eDP7VIcsAGlqjTYQDWdkADMYR1m-jJ0WXBj6niZwZhay4BJqohTUDh7G3oiomDiYANqh5pbgtvYAukHGMAAeBAle7sl+aYEGrhRgwFBWMMy0IMBxEAA89DDUJVAWQpoAStSgtEI9zoze9NwwYBT8AEZ0AHybABR88xS0WeFWEBxEMOnWMDJgMHML+Wx85NeDw2CjptkRNnaaAH5ltQAG50GDPcjkehXfIDIYjTTHHJgKIxOIwIFgUHgyFQmHWfJ8ZQASmIfFo1CghzuFwAdAyDkcwiiIEsGXTqqkAjBlPplAYAPSCmAAWnFEslUulMtlctFJGFUOVKq0IBAoiWAHMQOoABbUNRCRUi+Vm80WkjaaACRgAYRAtEpzWRv3ipHIoEaUFojA4AGZ-Sp1JprXoreAbaB1rdqEJXadyu1Ot0dvx7Y7nVAE5GSfoTWKLUXixKC6qYGg1AI1MMllA9TWYCDorEbppKTRaGDjUqS33LWHbQgwM3RDIhGjYjnIOUPWrvb6OJxgxo1Tp9IO1kbp-Fk10IBA04xh6Px5PqDu80LTf3b7Ky6rK9Xax4G7A5m2YB26N2C3f-1KEY6EOI7ROOeQ7rOfBUHQTCsBwADsACcK6hpG4abtuLJugoSYdPuh7pieYFCBB2GnFeBiDoMaj8Mw4h4SmB47HOagBrw5BrIoHECMIYiSNIVgBioJJCgAVDADRNC04BtPh3Q9HO5BsTA-r6MqXEwAo6lQoIIjiFItC-BwKzrHQ-K7EyO7nC86gBjpnEmTitAObx+kCUZQnAmCLkiTZyoqWptmadiPmuXp-GGcZ3nmcGABk5LKpSRrgKIjCUJ2cHsFwrnJUIqXpRFBmCfIWn8jAYmCkAA)
英文:

One approach looks like this:

  1. function process&lt;T extends Record&lt;keyof T, number&gt;&gt;(
  2. yourDimensions: { [K in keyof T]:
  3. K extends DimensionKeys ? never :
  4. T[K] extends DimensionValues ? never :
  5. T[K]
  6. }) {
  7. 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&lt;keyof T, number&gt;, 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:

  1. const example = process({
  2. a: 3, // okay
  3. b: 2, // error
  4. moduleVersion: 3 // error
  5. })
  6. /* function process&lt;{
  7. a: 3;
  8. b: 2;
  9. moduleVersion: number;
  10. }&gt;(yourDimensions: {
  11. a: 3;
  12. b: never;
  13. moduleVersion: never;
  14. }) */

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

huangapple
  • 本文由 发表于 2023年6月29日 03:19:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576131.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定