Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic parameter type is not enforced #17655

Closed
ownerer opened this issue Aug 7, 2017 · 8 comments
Closed

Generic parameter type is not enforced #17655

ownerer opened this issue Aug 7, 2017 · 8 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@ownerer
Copy link

ownerer commented Aug 7, 2017

TypeScript Version: 2.4.0

Code

class Base<TParams> {
	private params: TParams; //cfr https://github.com/Microsoft/TypeScript/issues/1396
}
class Params {
	constructor(public test: string) {
		
	}
}
class Component extends Base<Params> {

}

function testInference<TParams>(component: Base<TParams>, params: TParams) {

}

var component = new Component();

testInference(component, {  });

Expected behavior:
The second parameter of the call to the "testInference" function should be coerced to be of type "Params".

Actual behavior:
Any type of variable can be passed.

@sylvanaar
Copy link

sylvanaar commented Aug 7, 2017

You problem is that you pass 2 types for TParams and the type Params is assignable to {} so {} is taken to be the type of TParams

var v1 : {} = new Params("foo")

If the types are incompatible, then the first match from left to right is used:

class Base<TParams> {
	private params: TParams; //cfr https://github.com/Microsoft/TypeScript/issues/1396
}
class Params {
	constructor(public test: string) {
		
	}
}
class Component extends Base<Params> {

}

function testInference<TParams>(component: Base<TParams>, params: TParams) {

}

function testInference2<TParams>(params: TParams, component: Base<TParams>) {

}

var component = new Component();

testInference(component, { foo:1 });   // Error on foo
testInference2({ foo:1 }, component);  // Error on component

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Aug 7, 2017
@ownerer
Copy link
Author

ownerer commented Aug 7, 2017

What do you mean I'm "passing" 2 types for TParams"?
Clearly the type of TParams should be inferred from the first argument passed to the function, no?
And since that argument is of type "Component" and that type inherits "Base´Params´", I would expect the compiler to force every subsequent parameter that uses TParams in its type definition somehow to force it to be the same type as the firsts parameter that uses it, ie: defines it.
In this case: Params class, and nothing else, not even a supertype like {}.

@ajafff
Copy link
Contributor

ajafff commented Aug 7, 2017

@ownerer you are looking for #14829

@ownerer
Copy link
Author

ownerer commented Aug 7, 2017

Ha, well what do you know!
Thanks @ajafff !

Changing

function testInference<TParams>(component: Base<TParams>, params: TParams) {

}

To

function testInference<TParams>(component: Base<TParams>, params: TParams & {}) {

}

Works!

@ownerer
Copy link
Author

ownerer commented Aug 8, 2017

I wanted to get back to this again.
While the suggested solution works, I fail to see why the "& {}" intersection should ever be explicitly written in the first place.
Shouldn't this be the default behavior?
Or in other words: can anyone tell me why or in which scenario(s) the observed behavior without the "& {}" intersection would ever be intended/useful?

@sylvanaar
Copy link

sylvanaar commented Aug 8, 2017

How does it know the correct value of your generic parameter if you pass 2 different values for it? The first one? Well who says that the first one is the one I want, what if it is the second one. You can't make such an assumption like that.

The type inference mechanism tries to solve for a correct type based on the types supplied and the type definition. If it can it goes with that - if not you get an error.

Here is a nicer way of writing it

class Base<TParams> {
	private params: TParams; //cfr https://github.com/Microsoft/TypeScript/issues/1396
}
class Params {
	constructor(public test: string) {
		
	}
}
class Component extends Base<Params> {

}

type InferLast<T> = T & {}

function testInference<TParams>(component: Base<TParams>, params: InferLast<TParams>) {

}

function testInference2<TParams>(component: Base<InferLast<TParams>>, params: TParams) {

}

var component = new Component();

testInference(component, { });   // Error on foo
testInference2(component, { foo:1 });  // Error on component

@ownerer
Copy link
Author

ownerer commented Aug 8, 2017

Hmmmm, ok, thanks for the clarification.
I understand now what you originally meant by passing 2 types for TParams!

@mhegazy
Copy link
Contributor

mhegazy commented Aug 22, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Aug 22, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants