我可以强制执行 属性 类型的唯一性吗?

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对所有AA)。

因此,当您将 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 方法,请参阅 了解如何执行此操作以及为什么不应该这样做)。

Playground link to code