admin管理员组文章数量:1391964
The issue happens when using the function use
with one of its arguments of type MiddleWare<Record<string, unknown>>
, so I am expecting it to infer custom entries with key of type string and some inferred value of some type. But in reality I get compile error when trying to use the use
function
For example when trying to pass MiddleWare<{a: number}, {x: number}, TestEvents>
to the function which takes arguments of type MiddleWare<Record<string, unknown>, {x: number}>>
I get the error related to one of the MiddleWare interface callbacks process
:
Types of property process are incompatible.
Type '(ctx: Context<{ a: number; }, TestEvents>) => { x: number; } | Promise<{ x: number; }>' is not assignable to type '(ctx: Context<Record<string, unknown>, TestEvents>) => { x: number; } | Promise<{ x: number; }>'.
Types of parameters ctx and ctx are incompatible.
Type Context<Record<string, unknown>, TestEvents> is not assignable to type Context<{ a: number; }, TestEvents>
Property 'a' is missing in type Context<Record<string, unknown>, TestEvents> but required in type { a: number; }
The related code looks like this:
export type MiddleWare<
TInput extends Record<string, unknown>,
TOutput extends Record<string, unknown>,
TEvents extends EventMap,
> = {
name: string
deps?: (keyof TInput & string)[]
provides: keyof TOutput & string
process: (ctx: Context<TInput, TEvents>) => Promise<TOutput> | TOutput
rollback?: (ctx: Context<TInput & TOutput, TEvents>) => Promise<void> | void
}
And the method which takes the MiddleWare argument
export const createPipeline = <
TEvents extends EventMap = {},
TInitial extends Record<string, unknown> = Record<string, unknown>,
>(): Pipeline<
TInitial,
Record<string, unknown>,
TEvents
> => {
const middlewares: MiddleWare<
Record<string, unknown>,
Record<string, unknown>,
TEvents
>[] = []
const eventSystem = createEventSystem<TEvents>()
return {
async execute(input) {
let ctx = createInitialContext(input, eventSystem)
const hookSystem = createHookSystem<typeof ctx, TEvents>()
try {
await hookSystem.trigger('before', ctx)
for (const mw of middlewares) {
// Type assertion for middleware compatibility
ctx = await processMiddleware(
mw as unknown as MiddleWare<typeof ctx, any, TEvents>,
ctx,
)
Object.freeze(ctx)
}
await hookSystem.trigger('after', ctx)
return ctx as Context<any, TEvents>
} catch (error) {
const errorCtx: Context<Record<string, unknown>, TEvents> = {
...ctx,
$error: error as Error,
$hooks: createHookSystem<
Record<string, unknown>,
TEvents
>(),
}
await hookSystem.trigger(
'error',
errorCtx as Context<any, TEvents>,
)
throw errorCtx
}
},
use<TNew extends Record<string, unknown>>(
mw: MiddleWare<Record<string, unknown>, TNew, TEvents>,
) {
middlewares.push(mw as MiddleWare<any, any, TEvents>)
return this as unknown as Pipeline<
TInitial, // try to tinker unknown / never
Record<string, unknown> & TNew,
TEvents
>
},
events: eventSystem,
hooks: createHookSystem<Record<string, unknown>, TEvents>(),
}
}
Here is how the usage looks like:
// First middleware: adds property 'x'
// no issues no errors, context infers and looks fine, no any values etc
const mw1: MiddleWare<{ 'a': number }, { 'x': number }, TestEvents> = {
name: 'addX',
provides: 'x',
process: (ctx) => ({ ...ctx, 'x': ctx['a'] + 10 }),
}
// Second middleware: uses 'x' to produce property 'y'
// same situation here
const mw2: MiddleWare<{ a: number; x: number }, { y: number }, TestEvents> = {
name: 'addY',
deps: ['x'],
provides: 'y',
process: (ctx) => ({ ...ctx, y: ctx.x * 2 }),
}
const pipeline = createPipeline<TestEvents>()
// Use both middlewares in the pipeline.
// ! TS2345 !
pipeline
.use(mw1) // <{a: number},{x: number},TestEvents> not assignable to <Record<string, unknown>,...>
.use(mw2)
// Execute the pipeline.
const input = { a: 5 }
const result = await pipeline.execute(input)
I tried to tinker never
and unknown
types in use
and execute
functions to instantiate empty value at first and then assign new types to it, but it didn't work
The issue happens when using the function use
with one of its arguments of type MiddleWare<Record<string, unknown>>
, so I am expecting it to infer custom entries with key of type string and some inferred value of some type. But in reality I get compile error when trying to use the use
function
For example when trying to pass MiddleWare<{a: number}, {x: number}, TestEvents>
to the function which takes arguments of type MiddleWare<Record<string, unknown>, {x: number}>>
I get the error related to one of the MiddleWare interface callbacks process
:
Types of property process are incompatible.
Type '(ctx: Context<{ a: number; }, TestEvents>) => { x: number; } | Promise<{ x: number; }>' is not assignable to type '(ctx: Context<Record<string, unknown>, TestEvents>) => { x: number; } | Promise<{ x: number; }>'.
Types of parameters ctx and ctx are incompatible.
Type Context<Record<string, unknown>, TestEvents> is not assignable to type Context<{ a: number; }, TestEvents>
Property 'a' is missing in type Context<Record<string, unknown>, TestEvents> but required in type { a: number; }
The related code looks like this:
export type MiddleWare<
TInput extends Record<string, unknown>,
TOutput extends Record<string, unknown>,
TEvents extends EventMap,
> = {
name: string
deps?: (keyof TInput & string)[]
provides: keyof TOutput & string
process: (ctx: Context<TInput, TEvents>) => Promise<TOutput> | TOutput
rollback?: (ctx: Context<TInput & TOutput, TEvents>) => Promise<void> | void
}
And the method which takes the MiddleWare argument
export const createPipeline = <
TEvents extends EventMap = {},
TInitial extends Record<string, unknown> = Record<string, unknown>,
>(): Pipeline<
TInitial,
Record<string, unknown>,
TEvents
> => {
const middlewares: MiddleWare<
Record<string, unknown>,
Record<string, unknown>,
TEvents
>[] = []
const eventSystem = createEventSystem<TEvents>()
return {
async execute(input) {
let ctx = createInitialContext(input, eventSystem)
const hookSystem = createHookSystem<typeof ctx, TEvents>()
try {
await hookSystem.trigger('before', ctx)
for (const mw of middlewares) {
// Type assertion for middleware compatibility
ctx = await processMiddleware(
mw as unknown as MiddleWare<typeof ctx, any, TEvents>,
ctx,
)
Object.freeze(ctx)
}
await hookSystem.trigger('after', ctx)
return ctx as Context<any, TEvents>
} catch (error) {
const errorCtx: Context<Record<string, unknown>, TEvents> = {
...ctx,
$error: error as Error,
$hooks: createHookSystem<
Record<string, unknown>,
TEvents
>(),
}
await hookSystem.trigger(
'error',
errorCtx as Context<any, TEvents>,
)
throw errorCtx
}
},
use<TNew extends Record<string, unknown>>(
mw: MiddleWare<Record<string, unknown>, TNew, TEvents>,
) {
middlewares.push(mw as MiddleWare<any, any, TEvents>)
return this as unknown as Pipeline<
TInitial, // try to tinker unknown / never
Record<string, unknown> & TNew,
TEvents
>
},
events: eventSystem,
hooks: createHookSystem<Record<string, unknown>, TEvents>(),
}
}
Here is how the usage looks like:
// First middleware: adds property 'x'
// no issues no errors, context infers and looks fine, no any values etc
const mw1: MiddleWare<{ 'a': number }, { 'x': number }, TestEvents> = {
name: 'addX',
provides: 'x',
process: (ctx) => ({ ...ctx, 'x': ctx['a'] + 10 }),
}
// Second middleware: uses 'x' to produce property 'y'
// same situation here
const mw2: MiddleWare<{ a: number; x: number }, { y: number }, TestEvents> = {
name: 'addY',
deps: ['x'],
provides: 'y',
process: (ctx) => ({ ...ctx, y: ctx.x * 2 }),
}
const pipeline = createPipeline<TestEvents>()
// Use both middlewares in the pipeline.
// ! TS2345 !
pipeline
.use(mw1) // <{a: number},{x: number},TestEvents> not assignable to <Record<string, unknown>,...>
.use(mw2)
// Execute the pipeline.
const input = { a: 5 }
const result = await pipeline.execute(input)
I tried to tinker never
and unknown
types in use
and execute
functions to instantiate empty value at first and then assign new types to it, but it didn't work
- i suggest to recreate the problem on typescriptlang./play . otherwise it's hard to debug it given number of undefined types/functions – Alexander Nenashev Commented Mar 14 at 9:08
1 Answer
Reset to default 0solved it by adding a helper function to create middleware
type InferInput<TProcess> = TProcess extends (ctx: Context<infer I, any>) => any
? I
: never
type InferOutput<TProcess> = TProcess extends (
ctx: Context<any, any>,
) => infer O
? O
: never
export function defineMiddleware<
TProcess extends (ctx: any) => any,
TEvents extends Record<string, (...args: any[]) => void> = {},
>(mw: {
name: string
deps?: (keyof InferInput<TProcess> & string)[]
provides: keyof InferOutput<TProcess> & string
process: TProcess
rollback?: (
ctx: Context<InferInput<TProcess> & InferOutput<TProcess>, TEvents>,
) => void | Promise<void>
}): MiddleWare<InferInput<TProcess>, InferOutput<TProcess>, TEvents> {
return mw as MiddleWare<
InferInput<TProcess>,
InferOutput<TProcess>,
TEvents
>
}
本文标签:
版权声明:本文标题:TypeScript TS2345 error when passing {a: number} to Record<string, unknown> or Record<string, never> 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744666556a2618567.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论