打字稿中的通用约束违反不相容性
我正在尝试将标记的联合建模为数据。
首先,我创建一个 Tagged
实用程序类型,以表示与标记字段相交的任何内容:
type Tagged<T, With extends PropertyKey> = T & { _tag: With };
然后,我创建给定类型的表示形式。
class TypeRep<T = any> {
T!: T;
constructor(readonly x: (value: T) => void) {}
}
我们的字段表示扩展了 TypeRep
,如下所示:
class FieldRep<
Key extends PropertyKey = PropertyKey,
Value extends TypeRep = TypeRep,
> extends TypeRep<Record<Key, Value["T"]>> {}
记录:
class RecordRep<FieldEncoders extends FieldRep[]>
extends TypeRep<UnionToIntersection<FieldEncoders[number]["T"]>>
{}
最后,我们的标记联合类型表示:
class TaggedUnionRep<
Tag extends PropertyKey = PropertyKey,
FieldReps extends FieldRep[] = FieldRep[],
> extends TypeRep<Tagged<RecordRep<FieldReps>["T"], Tag>> {}
这看起来一切都很好......除了我们不能分配狭窄的 TaggedUnionRep
实例到加宽类型:/
declare const a: TaggedUnionRep<"A", []>;
const x: TaggedUnionRep<PropertyKey, FieldRep[]> = a;
当然,我们在 x
下得到以下逆变错误:
Type 'TaggedUnionRep<"A", []>' is not assignable to type 'TaggedUnionRep<PropertyKey, FieldRep<PropertyKey, TypeRep<any>>[]>'.
Types of property 'x' are incompatible.
Type '(value: { _tag: "A"; }) => void' is not assignable to type '(value: Tagged<Record<PropertyKey, any>, PropertyKey>) => void'.
Types of parameters 'value' and 'value' are incompatible.
Type 'Tagged<Record<PropertyKey, any>, PropertyKey>' is not assignable to type '{ _tag: "A"; }'.
Types of property '_tag' are incompatible.
Type 'PropertyKey' is not assignable to type '"A"'.
Type 'string' is not assignable to type '"A"'.(2322)
我将非常感谢有关最佳约束方法的任何提示FieldReps
(理想情况下不要通过将 Tag
扩大到 any
)。
<一href="https://www.typescriptlang.org/play?#code/C4TwDgpgBAKghgcwRAJgHhgGigdQJbAAWUEAHsBAHYoDOUACgE4D2kjoA0hCAHxQC8sKADIoAbygB9YIgBcuAsQC+AbgBQa0JCgBVSnmaUYzAJKUKjGhADGwA5 Qx9BAChglyVWlDiUQUAPxQzqTyMACUAnw+fvKUEABuEIwrZBTUdMHyeJQAZklQAEoR-FG+AYVQsQlJ6mrWADZ wNHQw4BAFEGAYAt6+fGJqULAAhKHqQ9aGNMCMAK62zIzOjBBwKIb1fiFB8XD1sxChxXzxzHgoEWJKatd1jc1Q AGJ4EPUoHV2DUFx+qZ50TFYSU43B6gLYIJAmC+ADU9gd3GkvK1IB8eij2p1oXw-ulYG0PmgOpNGOgftg4fsIA BTABEMFpAF0eP1bg0mnRiYt3p00M9XigAKKUSYoJJ0XFeflvD7U5lfSUtAm8vT2YxmCxWWz2PkvN7C0Xi6mUW YAWWARklGXSGcyeGorhp2Q94EhUKrDISvq7Ef8GCwISAfmCA8Cg9xoUNpTywBKPHjo7LGT1E505djfXiMYTXc h0FzSYTU7GeDamdhXSzxLcxeyVlBJpRpt5Qog8x7KITaQBBWnYOU8dSN5vbXPu-Se3ngsPkp56mMDnpwFRAA" rel="nofollow noreferrer">这是上面示例的复制。
谢谢你!
I'm trying to model tagged unions as data.
First I create a Tagged
utility type, to represent anything intersected with the tag field:
type Tagged<T, With extends PropertyKey> = T & { _tag: With };
Then I create a representation of a given type.
class TypeRep<T = any> {
T!: T;
constructor(readonly x: (value: T) => void) {}
}
Our representation of fields extends the TypeRep
like so:
class FieldRep<
Key extends PropertyKey = PropertyKey,
Value extends TypeRep = TypeRep,
> extends TypeRep<Record<Key, Value["T"]>> {}
Records:
class RecordRep<FieldEncoders extends FieldRep[]>
extends TypeRep<UnionToIntersection<FieldEncoders[number]["T"]>>
{}
And finally, our tagged union type representation:
class TaggedUnionRep<
Tag extends PropertyKey = PropertyKey,
FieldReps extends FieldRep[] = FieldRep[],
> extends TypeRep<Tagged<RecordRep<FieldReps>["T"], Tag>> {}
This is all well and good it seems... except that we cannot assign a narrow TaggedUnionRep
instance to the widened type :/
declare const a: TaggedUnionRep<"A", []>;
const x: TaggedUnionRep<PropertyKey, FieldRep[]> = a;
Surely enough, we get the following contravariance error beneath x
:
Type 'TaggedUnionRep<"A", []>' is not assignable to type 'TaggedUnionRep<PropertyKey, FieldRep<PropertyKey, TypeRep<any>>[]>'.
Types of property 'x' are incompatible.
Type '(value: { _tag: "A"; }) => void' is not assignable to type '(value: Tagged<Record<PropertyKey, any>, PropertyKey>) => void'.
Types of parameters 'value' and 'value' are incompatible.
Type 'Tagged<Record<PropertyKey, any>, PropertyKey>' is not assignable to type '{ _tag: "A"; }'.
Types of property '_tag' are incompatible.
Type 'PropertyKey' is not assignable to type '"A"'.
Type 'string' is not assignable to type '"A"'.(2322)
I'd be greatly appreciative of any tips on best approach to constraining FieldReps
(ideally not through widening Tag
to any
).
Here is a reproduction of the example above.
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题是他们的属性
x
不匹配。在您的示例中,
ax
是一个函数,该函数采用{_tag:string}
之类的对象,但是xx
是一个函数,该函数采用类似对象{_tag:propertyKey}
。如:如果要分配
a
<代码> x ,则x
的类型将允许您传递>的数字或符号_tag
,在运行时可能无法胜任。似乎没有类型安全的方法可以允许
x
成为任何taggedunionrep
而无需更改类型的定义。unionTo Intertersection&lt; propertyKey&gt;
(即从不
)出于类似的原因而发生错误,未知>未知
在那里不合法。如果您真的知道自己在做什么,并且可以完全确定这是安全的,则可以将
strictfunctionTypes
设置为false
在tsconfig.json中以禁用此诊断。The problem is that their properties
x
don't match.In your example,
a.x
is a function that takes an object like{ _tag: string }
, butx.x
is a function that takes an object like{ _tag: PropertyKey }
. As in:If you were to assign
a
tox
, the type ofx
would allow you to pass a number or symbol for_tag
, which may not be well-behaved at runtime.It appears there is no type-safe way to allow
x
to be anyTaggedUnionRep
without changing the definitions of your types.UnionToIntersection<PropertyKey>
(i.e.never
) errors for a similar reason, andunknown
isn't legal there.If you really know what you're doing and can be completely sure this is safe, you can set
strictFunctionTypes
tofalse
in your tsconfig.json to disable this diagnostic.