Typescript 仅在重载签名中获取错误2589
我有一个递归泛型类型定义,当它用作重载时会出错,但当它未重载或未在另一个泛型中使用时不会出错Typescript 仅在重载签名中获取错误2589,typescript,Typescript,我有一个递归泛型类型定义,当它用作重载时会出错,但当它未重载或未在另一个泛型中使用时不会出错 type JTDDataDef<S, D extends Record<string, unknown>> = | (// ref S extends { ref: string } ? JTDDataDef<D[S["ref"]], D> : // type S extend
type JTDDataDef<S, D extends Record<string, unknown>> =
| (// ref
S extends { ref: string }
? JTDDataDef<D[S["ref"]], D>
: // type
S extends { type: "string" }
? string
: // properties
S extends {
properties: Record<string, unknown>
}
? { -readonly [K in keyof S["properties"]]-?: JTDDataDef<S["properties"][K], D> }
:
never)
| (S extends { nullable: true } ? null : never)
type JTDDataType<S> = S extends { definitions: Record<string, unknown> }
? JTDDataDef<S, S["definitions"]>
: JTDDataDef<S, Record<string, never>>
interface ValidateFunction<T> {
(data: unknown): data is T
}
function compile1<S>(schema: S): ValidateFunction<JTDDataType<S>>
function compile1<T>(schema: unknown): ValidateFunction<T> {
return (() => true) as unknown as ValidateFunction<T>;
}
function compile2<S>(schema: S): ValidateFunction<JTDDataType<S>> {
return (() => true) as unknown as ValidateFunction<JTDDataType<S>>;
}
function compile3<S>(schema: S): (data: unknown) => data is JTDDataType<S>
function compile3<T>(schema: unknown): (data: unknown) => data is T {
return (() => true) as unknown as (data: unknown) => data is T;
}
但是,compile2
或compile3
不会引发任何错误
我的猜测是,由于使用ref
进行递归,该类型可能是无限的。但是,typescript实际上似乎可以很好地处理该类型,例如,如果我定义了一个无限递归类型:
const llSchema = {
definitions: {
node: {
properties: {
val: { type: "string" },
next: {
ref: "node",
nullable: true
}
}
}
},
ref: "node",
nullable: true,
} as const;
const isLinkedList = compile1(llSchema);
const list: unknown = null;
type LinkedList = { val: string; next: LinkedList; } | null;
function getNextVal(data: LinkedList): string | null {
return data && data.next && data.next.val;
}
if (isLinkedList(list)) {
const val = list && list.val;
const nextVal = getNextVal(list);
}
typescript实际上可以使用它,并且可以对它进行适当的类型检查,即使对于引发错误的重载函数也是如此。我想了解它为什么会抛出错误,特别是对于重载签名,以及除了在这种情况下看起来像重锤的/@ts expect error
之外,是否还有其他方法不抛出错误
[]请记住,重载是函数交叉点 修复
compile1
类型jtdatadef=
|(//参考
S扩展了{ref:string}
?jtdatadef
://类型
S扩展了{type:“string”}
一串
://属性
S延伸{
属性:记录
}
?{-readonly[K in keyof S[“properties”]]-?:jtdatadef}
:
(从未)
|(S扩展了{nullable:true}?null:never)
类型jtdatatype=S扩展了{定义:记录}
? jtdatadef
:jtdatadef
接口验证函数{
(数据:未知):数据为T
}
类型O=ValidateFunction
类型重载=
&((架构:jtdatatype)=>ValidateFunction)
&((模式:T)=>ValidateFunction)
const compile1:重载=(模式:T):ValidateFunction=>{
返回(()=>true)与ValidateFunction一样未知;
}
常量llSchema={
定义:{
节点:{
特性:{
val:{type:“string”},
下一步:{
参考:“节点”,
可为空:真
}
}
}
},
参考:“节点”,
可为空:是的,
}作为常量;
const isLinkedList=compile1(llSchema);
函数compile2(架构:S):ValidateFunction{
返回(()=>true)与ValidateFunction一样未知;
}
//这里有新的错误
函数compile3(架构:S):(数据:未知)=>数据为JTDDataType
函数compile3(模式:未知):(数据:未知)=>数据为T{
将(()=>true)作为未知返回(数据:未知)=>数据为T;
}
正如您可能已经注意到的,我没有更改compile3,但是我在这里遇到了一个错误
我认为这是因为当您出现第一个深度递归错误时,TS已经停止,并且没有检查compile2
和compile3
但这只是我的假设
让我们修复编译3
:
类型重载2=
&((架构:S)=>(数据:未知)=>数据为JTDDataType)
&((架构:未知)=>(数据:未知)=>数据为T)
const compile3:重载2=(架构:未知):(数据:未知)=>数据为T=>{
将(()=>true)作为未知返回(数据:未知)=>数据为T;
}
整体解决方案:
类型jtdatadef=
|(//参考
S扩展了{ref:string}
?jtdatadef
://类型
S扩展了{type:“string”}
一串
://属性
S延伸{
属性:记录
}
?{-readonly[K in keyof S[“properties”]]-?:jtdatadef}
:
(从未)
|(S扩展了{nullable:true}?null:never)
类型jtdatatype=S扩展了{定义:记录}
? jtdatadef
:jtdatadef
接口验证函数{
(数据:未知):数据为T
}
类型重载=
&((架构:jtdatatype)=>ValidateFunction)
&((模式:T)=>ValidateFunction)
const compile1:重载=(模式:T):ValidateFunction=>{
返回(()=>true)与ValidateFunction一样未知;
}
常量llSchema={
定义:{
节点:{
特性:{
val:{type:“string”},
下一步:{
参考:“节点”,
可为空:真
}
}
}
},
参考:“节点”,
可为空:是的,
}作为常量;
const isLinkedList=compile1(llSchema);
函数compile2(架构:S):ValidateFunction{
返回(()=>true)与ValidateFunction一样未知;
}
类型重载2=
&((架构:S)=>(数据:未知)=>数据为JTDDataType)
&((架构:未知)=>(数据:未知)=>数据为T)
const compile3:重载2=(架构:未知):(数据:未知)=>数据为T=>{
将(()=>true)作为未知返回(数据:未知)=>数据为T;
}
但老实说,我不认为函数类型的定义是好的
ValidateFunction
TS无法确定递归的深度。这根本不可能
,您可以找到有关
这不是一个“对任何人的隐性铸造”;它是一个递归保护,可以防止无限计算。如果S需要将自身与S进行比较以确定可分配性,然后S需要将自身与S进行比较,然后S需要将自身与S进行比较,然后S需要将自身与S进行比较,此时检查停止,并假设答案为“是”。事实上,这种情况在实践中确实出现了不少;如果您的模型依赖于任意深度的检查,那么实际上您要求编译器有时永久冻结对S>>>>>>的检查(这反过来将检查S>>>>)
我只使用了一个重载,因为它突出显示了错误,实际上有几个。你是正确的,compile3没有错误,typescript只是提前放弃了。有趣的是,为第一个添加了
ts expect error
,仍然阻止了第二个错误的触发。但是,我仍然感到困惑,因为你没有真正问题是什么
const llSchema = {
definitions: {
node: {
properties: {
val: { type: "string" },
next: {
ref: "node",
nullable: true
}
}
}
},
ref: "node",
nullable: true,
} as const;
const isLinkedList = compile1(llSchema);
const list: unknown = null;
type LinkedList = { val: string; next: LinkedList; } | null;
function getNextVal(data: LinkedList): string | null {
return data && data.next && data.next.val;
}
if (isLinkedList(list)) {
const val = list && list.val;
const nextVal = getNextVal(list);
}