作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在构建一个接受函数链的函数,其中每个函数都会转换前一个函数的结果。在给定的示例中,_context
应该等于 { hello: 'world', something: 'else' }
:
function withWorld<Context extends {}>(context: Context) {
return { ...context, hello: 'world' }
}
function withSomething<Context extends {}>(context: Context) {
return { ...context, something: 'else' }
}
test([withWorld, withSomething], (_context) => {})
但是,当我尝试用泛型来描述这种行为时,我偶然发现了一个错误:
No overload matches this call:
Types of parameters 'contextSomething' and 'contextB' are incompatible.
Type 'unknown' is not assignable to type '{}'.
这是代码(TypeScript playground):
export interface TestFunction {
<A>(
conditions: [(contextSolo: {}) => A],
body: (contextBodySolo: A) => any
): any
<A, B>(
conditions: [(contextA: {}) => A, (contextB: A) => B],
body: (contextBody: B) => void
): any
}
const test: TestFunction = () => void 0
function withWorld<Context extends {}>(contextWithWorld: Context) {
return { ...context, hello: 'world' }
}
function withSomething<Context extends {}>(contextSomething: Context) {
return { ...context, something: 'else' }
}
test([withSomething], (_context) => {})
test([withWorld], (_context) => {})
test([withWorld, withSomething], (_context) => {})
当数组中只有一个函数时,TypeScript 可以正确推断类型,即使示例更复杂并且具有初始状态 (TypeScript playground)。
完整的错误:
No overload matches this call.
Overload 1 of 2, '(conditions: [(contextSolo: {}) => any], body: (contextBodySolo: any) => any): any', gave the following error.
Argument of type '[<Context extends {}>(contextWithWorld: Context) => any, <Context extends {}>(contextSomething: Context) => any]' is not assignable to parameter of type '[(contextSolo: {}) => any]'.
Source has 2 element(s) but target allows only 1.
Overload 2 of 2, '(conditions: [(contextA: {}) => unknown, (contextB: unknown) => any], body: (contextBody: any) => void): any', gave the following error.
Type '<Context extends {}>(contextSomething: Context) => any' is not assignable to type '(contextB: unknown) => any'.
Types of parameters 'contextSomething' and 'contextB' are incompatible.
Type 'unknown' is not assignable to type '{}'.(2769)
最佳答案
这里有工作示例:
export interface TestFunction<Ctx> {
<A>(
conditions: [(context: Ctx) => A],
body: (context: A) => any
): any
<A, B>(
conditions: [(context: Ctx) => A & Ctx, (context: A & Ctx) => B],
body: (context: B & Ctx) => void
): any
}
const test: TestFunction<{ hello: 'world' }> = () => void 0
function withWorld<Context extends { hello: 'world' }>(context: Context) {
return { ...context, world: 'hello' }
}
function withSomething<Context extends {}>(context: Context) {
return { ...context, something: 'else' }
}
test([withSomething], (_context) => void 0)
test([withWorld], (_context) => void 0)
test([withWorld, withSomething], (_context) => void 0)
我明确地添加了 Ctx
作为每个参数的交集
更新
这里有通用的解决方案:
type Fn = (...args: any[]) => any
type MapObject<T extends Fn> = ReturnType<T>
type Elem = Fn
type Mapper<
Arr extends ReadonlyArray<Elem>,
Result extends Record<string, any> = {}
> = Arr extends []
? Result
: Arr extends [infer H]
? H extends Elem
? Result & MapObject<H>
: never
: Arr extends readonly [infer H, ...infer Tail]
? Tail extends ReadonlyArray<Elem>
? H extends Elem
? Mapper<Tail, Result & MapObject<H>>
: never
: never
: never
type Foo = { foo: 'foo' }
type Bar = { bar: 'bar' }
type Baz = { baz: 'baz' }
type Result = Mapper<[(arg: number) => Foo, (arg: Foo) => Bar, (arg: Bar) => Baz]> // Foo & Bar & Baz
export interface TestFunction<Ctx> {
<A>(
conditions: [(context: Ctx) => A],
body: (context: A) => any
): any
<A, B, C extends ReadonlyArray<any>>(
conditions: C,
body: (context: Mapper<C, Ctx>) => void
): any
}
const test: TestFunction<{ hello: 'world' }> = () => void 0
function withWorld<Context extends { hello: 'world' }>(context: Context) {
return { ...context, world: 'hello' }
}
function withSomething<Context extends {}>(context: Context) {
return { ...context, something: 'else' }
}
function withSomethingElse<Context extends {}>(context: Context) {
return { ...context, somethingElse: 'smth else' }
}
test([withSomething], (_context) => void 0)
test([withWorld], (_context) => void 0)
test([withWorld, withSomething, withSomethingElse] as const, (_context) => void 0)
Here您可以找到您可能感兴趣的其他示例
可变和不可变数组
const mutable1 = [1, 2] // number[]
const mutable2 = [{ age: 1 }, { name: 'John' }]
type MutableLength = (typeof mutable2)['length'] // number, we don't know the length
// const mutable2: ({
// age: number;
// name?: undefined;
// } | {
// name: string;
// age?: undefined;
// })[]
// As you see, if you want to operate on mutable array, TS will just make a union type from all array customElements
const immutable = [{ age: 1 }, { name: 'John' }] as const
type ImmutableLength = (typeof immutable)['length'] // length is 2, we know exactly the length of array and the type of all elements
// Here, TS is able to say that your array has exact two elements
UPDATE,我希望最后 :D
我的错,我认为用可变数组是不可能的,但我只需要从不同的角度看问题。
这是可行的解决方案:
type Fn = (...args: any[]) => any
// credits goes https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
export interface TestFunction<Ctx> {
<A>(
conditions: [(context: Ctx) => A],
body: (context: A) => any
): any
<C extends Array<Fn>>(
conditions: C,
body: (context: UnionToIntersection<ReturnType<C[number]>>) => void
): any
}
const test: TestFunction<{ hello: 'world' }> = () => void 0
function withWorld<Context extends { hello: 'world' }>(context: Context) {
return { ...context, world: 'hello' }
}
function withSomething<Context extends {}>(context: Context) {
return { ...context, something: 'else' }
}
function withSomethingElse<Context extends {}>(context: Context) {
return { ...context, somethingElse: 'smth else' }
}
test([withSomething], (_context) => void 0)
test([withWorld], (_context) => void 0)
test([withWorld, withSomething, withSomethingElse], (_context) => void 0)
关于typescript - 无法使 TypeScript 泛型链工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66096137/
我是一名优秀的程序员,十分优秀!