我可以强制执行 属性 类型的唯一性吗?
Can I enforce property-type uniqueness?
我想保证 T 中不超过一个类型为 P 的字段。我在想这样的事情:
declare function f<T, U extends Unique<T, string>>(t: T);
所以
f({a : '', b: 0, c: 0})
编译但
f({a : '', b: 0, c: 0, d: ''})
没有。
我在想一些来自
type R = (keyof { a : string } ) ['length']
但是R
这里是number
不是字面上的1
.
编辑:我需要能够指定类型 P,这样最多有一个该类型或子类型的字段。
如果您希望 Unique<T, V>
扩展 T
当且仅当 T
中最多有一个 属性 可分配给 V
,那么我认为下面的公式用最少的奇怪的边缘情况来做到这一点(尽管奇怪的边缘情况确实非常奇怪,所以你应该测试你关心的任何边缘情况):
type ProhibitProperty<T, V> = unknown extends {
[K in keyof T]: T[K] extends V ? unknown : never
}[keyof T] ? never : unknown;
type Unique<T extends object, V> = { [K in keyof T]:
T[K] & (T[K] extends V ? ProhibitProperty<Omit<T, K>, V> : unknown)
}
declare function f<T extends object>(t: Unique<T, string>): void;
想法:Unique<T, V>
将确保如果键 K
处的任何 属性 可分配给 V
,则 [=13= 的 属性 不会] 除了带键 K
的那个也可以分配给 V
。它通过检查 Omit<T, K> which is like T
but with the K
-keyed property removed. It intersects 属性 类型 T[K]
和 ProhibitProperty<Omit<T, K>, V>
.
来做到这一点
对于ProhibitProperty<T, V>
,如果T
的任何属性可分配给V
,输出类型将为the never
type, TypeScript's bottom type which absorbs all intersections (i.e., A & never
reduces to never
for all A
). On the other hand, if none of the properties of T
are assignable to V
, the output type will be the unknown
type,TypeScript的top类型被吸收到所有交叉点(即,A & unknown
对所有A
是A
)。
因此,当您将 T[K]
与 ProhibitProperty<Omit<T, K>, V>
相交时,您将得到 T[K]
,如果没有 T
的其他 属性 可分配给 V
,或 never
,如果 T
的任何其他 属性 可分配给 V
。哦,如果 T[K]
不可分配给 V
,我们只需将它与 unknown
相交,因为我们基本上不理会所有非 V
属性。
让我们确保它有效:
f({ a: '', b: 0, c: 1, d: 2 }); // okay
f({ a: '', b: 0, c: '' }); // error!
// ~ <------> ~
// string is not assignable to never
看起来不错。编译器接受具有一个 string
值的对象字面量,并拒绝具有多个 string
值的对象字面量。在错误情况下,它会抱怨两个 string
属性,这是我们能做的最好的,因为 TypeScript 中没有原则性的方法来选择联合的成员(尽管存在可怕的 hacky 方法,请参阅 了解如何执行此操作以及为什么不应该这样做)。
我想保证 T 中不超过一个类型为 P 的字段。我在想这样的事情:
declare function f<T, U extends Unique<T, string>>(t: T);
所以
f({a : '', b: 0, c: 0})
编译但
f({a : '', b: 0, c: 0, d: ''})
没有。
我在想一些来自
type R = (keyof { a : string } ) ['length']
但是R
这里是number
不是字面上的1
.
编辑:我需要能够指定类型 P,这样最多有一个该类型或子类型的字段。
如果您希望 Unique<T, V>
扩展 T
当且仅当 T
中最多有一个 属性 可分配给 V
,那么我认为下面的公式用最少的奇怪的边缘情况来做到这一点(尽管奇怪的边缘情况确实非常奇怪,所以你应该测试你关心的任何边缘情况):
type ProhibitProperty<T, V> = unknown extends {
[K in keyof T]: T[K] extends V ? unknown : never
}[keyof T] ? never : unknown;
type Unique<T extends object, V> = { [K in keyof T]:
T[K] & (T[K] extends V ? ProhibitProperty<Omit<T, K>, V> : unknown)
}
declare function f<T extends object>(t: Unique<T, string>): void;
想法:Unique<T, V>
将确保如果键 K
处的任何 属性 可分配给 V
,则 [=13= 的 属性 不会] 除了带键 K
的那个也可以分配给 V
。它通过检查 Omit<T, K> which is like T
but with the K
-keyed property removed. It intersects 属性 类型 T[K]
和 ProhibitProperty<Omit<T, K>, V>
.
对于ProhibitProperty<T, V>
,如果T
的任何属性可分配给V
,输出类型将为the never
type, TypeScript's bottom type which absorbs all intersections (i.e., A & never
reduces to never
for all A
). On the other hand, if none of the properties of T
are assignable to V
, the output type will be the unknown
type,TypeScript的top类型被吸收到所有交叉点(即,A & unknown
对所有A
是A
)。
因此,当您将 T[K]
与 ProhibitProperty<Omit<T, K>, V>
相交时,您将得到 T[K]
,如果没有 T
的其他 属性 可分配给 V
,或 never
,如果 T
的任何其他 属性 可分配给 V
。哦,如果 T[K]
不可分配给 V
,我们只需将它与 unknown
相交,因为我们基本上不理会所有非 V
属性。
让我们确保它有效:
f({ a: '', b: 0, c: 1, d: 2 }); // okay
f({ a: '', b: 0, c: '' }); // error!
// ~ <------> ~
// string is not assignable to never
看起来不错。编译器接受具有一个 string
值的对象字面量,并拒绝具有多个 string
值的对象字面量。在错误情况下,它会抱怨两个 string
属性,这是我们能做的最好的,因为 TypeScript 中没有原则性的方法来选择联合的成员(尽管存在可怕的 hacky 方法,请参阅