我遇到以下问题,我输入的函数“ Foo”如下所示。
function Foo(...cons: any[]) {
return function (this: {inject: any}) {
return (bar: any) => {
this.onChange // notice here how i have access to onChange
return reactC;
}
}
}
以上代码几乎是我想要的,基本上我希望添加的“ this”类型可以在“ bar”内部使用(忽略“ any”是我可以轻松获得的那些类型,我只希望正确键入“ this”。
实现如下。
export const TestImplementation: typeof Foo = Foo([Input])()(class TestClass {
testFunction() {
//this.inject
}
});
//“ void”类型的“ this”上下文不能分配给“ any”类型的“ this”方法
和'this.inject'不存在。
不要认为我的实现是正确的,而是在寻找一种方法来改变类中“ this”的类型 https://preview.tinyurl.com/ycc9c4l5 <有此问题的安全链接到游乐场
答案 0 :(得分:1)
从这个问题中并不清楚要实现什么,但是根据我的理解,您希望this
的类型可以通过Foo
函数针对作为参数传递的类进行修改。
除非通过继承,否则您不能为类的所有成员更改this
的类型。但是,您可以使用this
更改传递给函数的对象文字的ThisType<T>
类型。 ThisType<T>
是编译器的特殊标记(这很神奇),对于分配给T
的任何对象文字,编译器将使用this
作为ThisType<T>
的类型。 / p>
function Foo<T>(...cons: T[]) {
// Infer the type of the object literal passed in in TClass
// use ThisType to let the compiler know this should contain
// all memebers of the object literal plus a merger of all memebrs passed in to cons
return function <TClass>(classMember: TClass & ThisType<TClass & UnionToIntersection<T>>) : TClass & UnionToIntersection<T> {
return null as any; // implementation
}
}
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
class FakeClass {
inject: string; // ignore this property doesn't matter
}
const TestFoo = Foo(new FakeClass())({
TestTypes() {
this.inject // this has the members of FakeClass
}
})
TestFoo.TestTypes();
注意:此解决方案需要noImplicitThis: true
才能起作用。另外UnionToIntersection
是从here
如果要使用类,另一种选择是集中于继承部分。我们可以为您传递给Foo
的类创建一个类型来充当伪基类。您不能直接传递该类,您将需要传递一个Foo
调用的函数,该函数将带有用于新类的基类。
function Foo<T>(...cons: T[]) {
return function <TClass>(classMember: (base: Constructor<UnionToIntersection<T>>) => Constructor<TClass>) : Constructor<TClass> {
return classMember(class {
constructor() {
Object.assign(this, ...cons);
}
} as any)
}
}
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type Constructor<T> = new (...args: any[]) => T
class FakeClass {
inject: string;
}
const TestFoo = Foo(new FakeClass())(b=> class extends b {
TestTypes() {
this.inject
}
})
new TestFoo().TestTypes();
修改
根据我们在评论中讨论的内容,您希望结果成为一个类,并让其他类充当mixin。我们可以修改解决方案1来做到这一点
function Foo<T extends Constructor<any>[]>(cons: T) {
return function <TClass>(classMember: TClass & ThisType<TClass & MergeConstructorTypes<T>>): Constructor<TClass & MergeConstructorTypes<T>> {
// Naive implementation, you might want to do better here
var cls = class {
constructor() {
cons.forEach(c => c.apply(this));
}
};
cons.forEach(c => Object.assign(cls.prototype, c.prototype));
Object.assign(cls.prototype, classMember);
return cls as any;
}
}
type Constructor<T> = new (...args: any[]) => T
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type MergeConstructorTypes<T extends Constructor<any>[]> =
UnionToIntersection<InstanceType<T[number]>>;
class FakeClassA {
inject: string = "A"; // ignore this property doesn't matter
logA() {
console.log(this.inject);
}
}
class FakeClassB {
inject2: string = "B"; // ignore this property doesn't matter
logB() {
console.log(this.inject2);
}
}
const TestFoo = Foo([FakeClassA, FakeClassB])({
TestTypes() {
console.log(this.inject)
console.log(this.inject2)
this.logA();
this.logB();
}
})
new TestFoo().TestTypes();
注意在这种情况下,伪造的基类解决方案也可以使用,但是您对第一个解决方案表示了兴趣,因此我在这种情况下进行了举例说明。另外,上述解决方案使用3.0功能,并且再次需要noImplicitThis:true
希望它会有所帮助:)
答案 1 :(得分:0)
您好,将来所有看到这一点的人都应该感谢Titian早些时候有关“ this”的回答,因为它只能来自继承或操作ThisType,这对我来说是不可行的。我已经建好了。
export class InputMixin {
state = {
value: "asdsasads"
}
onChange(this: React.Component<any>, e: React.ChangeEvent<HTMLInputElement>) {
e.persist();
console.log(this, "THIS BINDING");
this.setState((prevState: any) => ({value: e.target.value}))
}
}
export class TestMixin {
state = {
myvalue: "myvalue"
}
}
export type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<T extends Constructor<any>[]>(constructors: T): Constructor<MergeConstructorTypes<T>> {
var cls = class {
state = {
}
constructor() {
constructors.forEach(c => {
const oldState = this.state;
c.apply(this);
this.state = Object.assign({}, this.state, oldState);
});
}
} as any;
constructors.forEach((c: any) => {
Object.assign(cls.prototype, c.prototype);
});
return cls as any;
}
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type MergeConstructorTypes<T extends Constructor<any>[]> =
UnionToIntersection<InstanceType<T[number]>>;
interface IProps {
bleet: string;
}
export class CounterTest extends Mixin([TestMixin, InputMixin, React.Component as React.ComponentClass<IProps, any>]) {
constructor(props: any) {
super(props);
this.state = {
...this.state,
newState: ""
};
this.onChange = this.onChange.bind(this);
}
render() {
return <Input value={this.state.value} onChange={this.onChange}/>
}
现在,您可以定义独立于组件的“ mixins”并本质上使用它们的代码,这将停止无限重复真正的基本功能,该功能来自于由父级控制所有事物的原理。您不再需要每个需要子级输入的父级上的onChange(),只需将InputMixin混合在一起,然后为您键入所有内容,而所有内容都已经存在。通过简单地从原型中吸取所有内容并将其重新绑定到另一个类。
此外,它将所有“状态”合并到一个最终状态中,以便多个mixin可以实现“ state”,并且最终类将获得正确键入的所有mixins的状态。
感谢Gitter,感谢Titian。