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

Resulting type of the object literal #16130

Closed
olegdunkan opened this issue May 29, 2017 · 5 comments
Closed

Resulting type of the object literal #16130

olegdunkan opened this issue May 29, 2017 · 5 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@olegdunkan
Copy link

I asked question about resulting type of the object literal at stackoverflow.
From specification:

When an object literal is contextually typed by a type that includes a string index signature, the resulting type of the object literal includes a string index signature with the union type of the types of the properties declared in the object literal, or the Undefined type if the object literal is empty

First, it sounds like some entity that has type can redefine it if it meets object literal.
Second, maybe it is all about hidden conversations at expression trees?

The question:
Is there any places in code where I can see resulting type of object literal in action?

I have got an answer:

declare const str: 'a' | 'b';
let foo = { a: 1, b: "2" }[str]; //type of foo string | number

Ok.
I look at following part of spec.

A bracket notation property access of the form
object [ index ]
where object and index are expressions, is used to access the property with the name computed by the index expression on the given object. A bracket notation property access is processed as follows at compile-time:

If index is a string literal or a numeric literal and object has an apparent property (section 3.11.1) with the name given by that literal (converted to its string representation in the case of a numeric literal), the property access is of the type of that property.
Otherwise, if object has an apparent numeric index signature and index is of type Any, the Number primitive type, or an enum type, the property access is of the type of that index signature.
Otherwise, if object has an apparent string index signature and index is of type Any, the String or Number primitive type, or an enum type, the property access is of the type of that index signature.
Otherwise, if index is of type Any, the String or Number primitive type, or an enum type, the property access is of type Any.
Otherwise, the property access is invalid and a compile-time error occurs.

The type of str is union of string literals and there is no branch for this case above.
And I didn't see any index signatures in the example (from answer).

But it does compile.

Could someone elaborate on this topic, please.

@Draqir
Copy link

Draqir commented May 30, 2017

This is a place for reporting and discussing issues, not a help forum.

@olegdunkan
Copy link
Author

@Draqir first of all I have asked the question before at stackoverflow and haven't got an answer.
Specification are situated in this repo and what specification states may be considered as an issue.
Maybe there is some contradiction in spec, but more likely it is my misunderstanding, it is like I suspect an error in code but code works like intended.
If my question won't be answered then no problems I'll understand this.

@DanielRosenwasser
Copy link
Member

First of all, the spec is definitely not up to date (it currently describes 1.8).

Is there any places in code where I can see resulting type of object literal in action?

declare function f<T extends { [x: string]: boolean | string | number }>(x: T): T;

export let x = f({
    a: 100,
    b: 200,
    c: 'hello'
})

Hover over the type of x (or get its --declaration emit.

The type of str is union of string literals and there is no branch for this case above.

This is mostly a symptom of the spec being out of date. With indexed access types, we changed a lot of the behavior here. Check out #11929 for specifics.

@DanielRosenwasser DanielRosenwasser added the Question An issue which isn't directly actionable in code label May 30, 2017
@olegdunkan
Copy link
Author

@DanielRosenwasser thanks for example.

If you are taking about x variable in let declaration then tsc shows me the object type like

export declare let x: {
    a: 100;
    b: 200;
    c: "hello";
};

If you are talking about x parameter of f then the type is like in extends clause { [x: string]: boolean | string | number }

Let me make parsing of your example applying next section of spec to understand terms in the same way:

When an object literal is contextually typed by a type that includes a string index signature, the resulting type of the object literal includes a string index signature with the union type of the types of the properties declared in the object literal, or the Undefined type if the object literal is empty

Object literal is { a: 100, b: 200, c: "hello" }
Contextually typed by a type is { [x: string]: boolean | string | number }
The resulting type of the object literal includes a string index signature with the union type of the types of the properties declared in the object literal is {[key:string]:number|string}
If I made parsing right then I have to expect the type of x variable as {[key:string]:number|string} and it solves my case and infers from spec.

@olegdunkan
Copy link
Author

olegdunkan commented Jun 1, 2017

@DanielRosenwasser
Now I see that resulting type of object is hidden (computed in background to compare it to contextually typed by a type ).
The goal of computing of resulting type of object to compare it to index signature type (if any exists) for validity.
To compare only, not to redefine previous type.
Maybe it is better to focus attention on the purpose of resulting type of object in spec.
And we can't see resulting type of object in the code.
We can see duality of behavior of object literal expression from your example.
Inside f local x has type { [x: string]: boolean | string | number }.
Variable x has type { a: number, b: number, c: string }.
More than this we have third type - resulting type of object is { [x: string]: string | number } in the background.

https://github.com/Microsoft/TypeScript/blob/master/src/compiler/checker.ts#L13412

const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);

@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

3 participants