问题
This is somewhat the transformation of a question I posted somewhere else yesterday.
My objective is not to get a working result but to have a better understanding of the kind of design I can obtain while returning the correct types. This by using here a minimalist example, so please don't tell me it's useless or does nothing.
Code sample (Try it in Typescript playground):
interface TestInterface {
test: () => {}
}
class Obj implements TestInterface{
test() {
return { test: 'test' }
}
}
class Wrapper<T extends TestInterface> {
constructor(public obj: T) {
}
test<T>() {
return this.obj.test();
}
test2<T extends TestInterface>(obj: T) {
return obj.test();
}
}
let w = new Wrapper(new Obj());
let r1 = w.obj.test();
let r2 = w.test();
let r3 = w.test2(new Obj);
Here:w
has the type Wrapper<Obj>
r1
has the type {test: string}
w.test
has the type Wrapper<Obj>.test: () => {}
r2
has the type {}
r3
has the type {}
This case is a class that stores an object in a property. It proxies the calls to this object.
What interest me is the return type of r2
. And specifically the fact that I would like to find a way to return the same type as for r1
without specifying the type in the three bottom lines. r3
is another test, by passing directly the generic type. The result is the same.
I have some questions about that:
- From my understanding the
Wrapper.test
return type is resolved before theOBJ
generic has any influence. So its base value is used (in this case{}
which is the result ofTestInterface.Test
). Is it right? - Is it something done on purpose, some limitation, some upcoming feature in future TS versions? (didn't see anything about that)
- And mainly, how to be able to forward embedded object methods return types (or compose a new object with them), considering that in such a proxy, it should be possible to plug anything that respect the
TestInterface
. And without putting generics everywhere in the caller (at the bottom lines), that I know how to do.
I've seen some object builder in this post: https://github.com/Microsoft/TypeScript/pull/14141. Maybe that's the direction I'll take.
回答1:
I think this is what you want:
interface TestInterface<T1,T2> {
test: () => T1
test2: () => T2
}
class Obj implements TestInterface<{ test: string }, { test2: number }>{
test() {
return { test: 'test' }
}
test2() {
return { test2: 2 }
}
}
class Wrapper<T1,T2> implements TestInterface<T1,T2> {
public obj :TestInterface<T1,T2>;
constructor(obj: { new (): TestInterface<T1,T2> }) {
this.obj = new obj();
}
test():T1 {
return this.obj.test();
}
test2():T2 {
return this.obj.test2();
}
}
let w = new Wrapper(Obj);
let r = w.test();
let s = w.test2();
回答2:
I answer to myself, because I like to use Stackoverflow as my personal notebook. If someone has better answers to provide, I'm a buyer!
- Apparently Typescript is a big couchpotato and doesn't follow the type inheritance. Maybe for performance reasons, maybe because the type could be affected by other means and wouldn't be reliable, maybe because it's tricky to do, maybe because it's not yet implemented, maybe because nobody asked for that because they don't use the model object, or Intellisense.
- Didn't see anything clear in the docs/upcoming features of Typescript 2.3 about that. There's a mention about strongly typing
this
here when using the--noImplicitThis
compilation option, and theThisType<T>
function to declarethis
explicitely. But apparently it's more about a function being aware of its embedding structure type, than following the object model flow. And it doesn't help in my case. - That, I can answer!
Here is a code sample (Test it in Typescript playground):
interface TestInterface {
test: () => {}
}
class Obj implements TestInterface{
test() {
return { test: 'test' }
}
}
class Wrapper<T extends TestInterface, TEST> {
public obj;
constructor(obj: { new (): T; prototype: { test: TEST } }) {
this.obj = new obj();
}
test():TEST {
return this.obj.test();
}
}
let w = new Wrapper(Obj);
let r = w.test();
In this case, r
type is {test: string}
. This solution consists in not instantiating Obj
but getting its prototype to deduce the type.
While searching, I also fell on a simpler way to write it by there (see update 2).
It gives the possibility to keep clever typings and mainly code completion when using some dynamic patterns. Now, it's still a choice to do to choose between completion or a smarter/clearer design...
This sample is based on an example from the Typescript handbook, at the bottom of this page.
来源:https://stackoverflow.com/questions/43505888/typescript-object-wrapping-typings-and-generics