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

Suggestion: Infer literal types for getter only properties that return literals #11467

Closed
aluanhaddad opened this issue Oct 9, 2016 · 8 comments
Labels
Suggestion An idea for TypeScript Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@aluanhaddad
Copy link
Contributor

aluanhaddad commented Oct 9, 2016

Suggestion: Infer literal types for getter only properties that return literals.

This suggestion arose from reading #11465 the example below comes directly from that issue.

TypeScript Version: nightly (2.0.0-dev.20161008)

Code

// A *self-contained* demonstration of the problem follows...
import React, { Component } from 'react';
import { StyleSheet, Text } from 'react-native';

export class Sample extends Component<{}, {}> {
    render() {
        return (
            <Text style={styles.text}>Hello TypeScript</Text> // error on styles, see below
        );
    }
}

const styles = StyleSheet.create({
    text: {
        position: 'absolute', // cause of the error: string instead of 'absolute'
        top: 0,
        left: 0     
    }
});

I tried a different workaround than the ones presented by @jovdb in #11465

const styles = StyleSheet.create({
  text: {
      get position() { return 'absolute'; },
      top: 0,
      left: 0
    }
});

Expected behavior:
The type of text.position is inferred as 'absolute'
Actual behavior:
The type of text.position is inferred as string

My reasoning here is that while the following rules, outlined in #10676, make complete sense in general, they are not ideal for getter only properties

  • In a function with no return type annotation, if the inferred return type is a literal type (but not a literal union type) and the function does not have a contextual type with a return type that includes literal types, the return type is widened to its widened literal type.
  • The type inferred for a property in an object literal is the widened literal type of the expression unless the property has a contextual type that includes literal types.

I am not sure if both of these rules apply here or only the second.

@HerringtonDarkholme
Copy link
Contributor

Probably a bug in spec, IMHO.
in #10676

The type inferred for a const variable or readonly property without a type annotation is the type of the initializer as-is.

And in #6532

A property declared with a get accessor and no set accessor is considered read-only.

An implicitly readonly property, namely get only accessor, should also be inferred as literal type if possible.

The type inferred for a property in an object literal is the widened literal type of the expression unless the property has a contextual type that includes literal types.

The above rule does not apply here because the contextual type Stylesheet.create does not have literal type.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Oct 10, 2016
@aluanhaddad
Copy link
Contributor Author

It is interesting to see the behavior in classes because both readonly qualified properties and get accessors can be explicitely defined.
image

image

@aluanhaddad
Copy link
Contributor Author

If #11535 gets adopted, this could be accomplished via

const styles = StyleSheet.create({
  text: Object.freeze({
      position: 'absolute',
      top: 0,
      left: 0
    })
});

@ghost
Copy link

ghost commented May 27, 2017

Possibly related:

function id<T>(x: T) { return x; }
function wrap<T>(value: T): { readonly value: T } { return { value }; }

const s: "s" = id("s"); // Works, inferred id<"s">
const vs: { readonly value: "s" } = wrap("s"); // Error, inferred wrap<string>

@aluanhaddad
Copy link
Contributor Author

aluanhaddad commented May 27, 2017

Interesting, it seems like the literal type is only inferred for id and not for wrap. I had to use an overload to make it typecheck

function wrap<T extends string>(value: T);
function wrap<T>(value: T);
function wrap<T>(value: T): { readonly value: T } { return { value }; }

@RyanCavanaugh RyanCavanaugh added Working as Intended The behavior described is the intended behavior; this is not a bug and removed In Discussion Not yet reached consensus labels Aug 12, 2017
@RyanCavanaugh
Copy link
Member

Experience in real code shows that getters sometimes have function-like behavior (where you would want the nonliteral type) and sometimes have property-like behavior (where you may want the literal type). In general, mostly due to back compat problems, we defer to nonliteral types when it isn't clear from context that you need one.

@aluanhaddad
Copy link
Contributor Author

@RyanCavanaugh thank you for explaining the reasoning, I really appreciate it.

@ghost
Copy link

ghost commented Aug 12, 2017

@RyanCavanaugh Shall I make a separate issue for my earlier comment?

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants