我有一个对象类型,它可以有任何它想要的属性,但是它的属性之一必须是一个函数(如果定义的话)。问题是:我希望此函数类型包含所有其他属性作为其参数。请参见the playground以更好地理解。

// https://github.com/microsoft/TypeScript/issues/14829#issuecomment-504042546
type NoInfer<T> = [T][T extends any ? 0 : never];

type Indexable = {
    [key: string]: any
}

type Events<T extends Indexable = Indexable> = {
    onFoo?: (arg: Omit<T, keyof Events>) => void
}

type Foo<T extends Indexable> = T & Events<NoInfer<T>>

declare function test<T extends Indexable>(foo: T & Foo<T>): void

test({
    a: 1,
    onFoo(arg) {
        // "typeof arg" should be "{ a: number }"
    }
})

我想这是循环类型的问题,因为this basic example也不起作用。

最佳答案

我不能让类型推断按你想要的方式工作。无论我做什么,要么T无法推断,要么它推断,但是onFoo属性的参数不能正确推断。我可能会认为这不是一个编译器错误和更多的设计限制。可能与onFoo参数没有真正的“AA>”有关。这可能与成功的类型推断算法需要多少个AA>有关。我真的不确定。我知道当我像这样和编译器战斗时,我通常会输。所以,尝试一些不那么有争议的事情可能是值得的。
一个更直接的方法是使用两个函数参数(我看到你不想做),如:

const makeOnFoo = <T>(
  foo: T,
  onFoo?: (arg: T) => void
): T & { onFoo?: (arg: T) => void } => Object.assign(foo, { onFoo });

这个函数将准确地推断出你想要的方式,我相信,推断出的arg将是T类型。如果您定义了这样的test()
declare function test<T>(
  foo: T & { onFoo?: (arg: Omit<T, "onFoo">) => void }
): void;

您至少可以使用makeFoo()来获得所需的推断:
test(
  makeOnFoo({ a: 1 }, arg => {
    console.log(arg.a.toFixed()); // okay
  })
);

我不知道你是否能接受,但至少它能更好地与编译器配合。
好吧,希望能有所帮助。祝你好运!
non-inferential type parameter usage site

关于typescript - 从其父对象推断方法类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57842079/

10-12 14:23