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

Type Inference Issues #15709

Closed
jmlopez-rod opened this issue May 9, 2017 · 4 comments
Closed

Type Inference Issues #15709

jmlopez-rod opened this issue May 9, 2017 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@jmlopez-rod
Copy link

TypeScript Version: 2.3.2

Code

interface IType {
  typeName: string;
}

interface IRoom {
  roomType: IType;
}

type DeepPartial<T> = {[P in keyof T]?: T[P] | (DeepPartial<T[P]> & object) };

// TS doesn't complain about t1 being assigned to roomType
const t1: { a: number } = { a: 0 };
const r1: DeepPartial<IRoom> = { roomType: t1 };

// Here it says that `{a: number}` cannot be assigned to the type I specified
const t2: IType | (DeepPartial<IType> & object) = { a: 0 };
const r2: DeepPartial<IRoom> = { roomType: t1 };

// Now it's not ok? But this was accepted for r1
const r3: DeepPartial<IRoom> = { roomType: { a: 0 } };

Expected behavior:

It should warn about the assignment to r1 as it does in r3.

Actual behavior:

Typescript does not warn me of a mistake when we declare r1. In the declaration of r3 it even says that {a: number} cannot be assigned to IType | (DeepPartial<IType> & object). Why doesn't it say the same thing for r1 in which I have provided t1 of type { a: number }?

@mhegazy
Copy link
Contributor

mhegazy commented May 9, 2017

The excess property checks, which is what reports the other errors in this sample, only works on object literals. the assumption is an object literal with extra properties is just wrong; this is not the case with references, since you might wasn't to use t1 as a roomType but also as some other type, think of var d:Dog can be used as an argument to something that expects an Animal.

I guess what you are looking for is exact types, where a type is closed and can not have any additional members.

@mhegazy mhegazy added the Duplicate An existing issue was already created label May 9, 2017
@jmlopez-rod
Copy link
Author

I guess that makes sense. Before I close this issue, could you tell me if the next example is related?

type DeepPartial<T> = {[P in keyof T]?: T[P] | (DeepPartial<T[P]> & object) };

type IName = {
  name: string;
}

type IType = {
  typeName: IName;
}

type IRoom =  {
  roomType: IType;
}

type IUser = {
  room: IRoom;
}

const p0: Partial<IUser> = { room: 0 }; // BAD - room should be IRoom | undefined
const p1: DeepPartial<IUser> = { room: 0 }; // BAD - room should can't be a number
const p2: DeepPartial<IUser> = { room: {} }; // OK - we have a DeepPartial<IRoom>
const p3: DeepPartial<IUser> = { room: { a: 0 } };  //BAD - `a` doesn't exists in IRoom
const p4: DeepPartial<IUser> = { room: { roomType: 0 } };  // BAD - roomType can't be a number
const p5: DeepPartial<IUser> = { room: { roomType: {} } };  // GOOD - there's a deep partial
const p6: DeepPartial<IUser> = { room: { roomType: { a: 0 } } };  // ? should be bad
const p7: DeepPartial<IUser> = { room: { roomType: { typeName: 0 } } };  // BAD - can't be number
const p8: DeepPartial<IUser> = { room: { roomType: { typeName: {} } } };  // GOOD - DeepPartial
const p9: DeepPartial<IUser> = { room: { roomType: { typeName: { a: 0 } } } };  // ? should be bad
const p10: DeepPartial<IUser> = { room: { roomType: { typeName: { name: 0 } } } };  // BAD - string
const p11: DeepPartial<IUser> = { room: { roomType: { typeName: { name: '0' } } } };  // GOOD

The playground is able to detect 6 correct errors, but I think it is missing 2 errors. Namely the ones I commented with ? should be bad. (p6 and p9).

I was trying to see if my definition of DeepPartials worked and that's how I came up with the more simplified example in this post.

In p6 for instance, shouldn't it say that a is not a valid member? It is an object literal, and it was able to do that for p3.

@mhegazy
Copy link
Contributor

mhegazy commented May 10, 2017

That is caused by the & object in your DeepPartial definition.

I think i understand why you added it, but i am afraid it does not do what you intended. you are running into a limitation that any object with only optional property is equivalent to {} which is the top type, and any thing is assignable to that, making it any for practical purposes. this issue is tracked by #7485

@mhegazy
Copy link
Contributor

mhegazy commented May 24, 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 May 24, 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
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants