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

typeof string literal #2639

Open
vkurchatkin opened this issue Oct 17, 2016 · 10 comments · May be fixed by #7607
Open

typeof string literal #2639

vkurchatkin opened this issue Oct 17, 2016 · 10 comments · May be fixed by #7607

Comments

@vkurchatkin
Copy link
Contributor

This is more of a question, than a bug report. Flow allows this:

const x = 'X';
const y: typeof x = 'Y';

It implies that inferred either typeof doesn't return the lowest possible, or that type of x is string, not 'X', although literal value of x is preserved:

const x = 'X';
const z: 'X' = x;

The question is: why it is implemented this way? It makes impossible to define literal type and instance of that type without duplication:

const Foo = 'Foo';
type FooT = typeof Foo; // doesn't do what you'd expect
@iamchenxin
Copy link
Contributor

Seems all of the literal types( string, number ...) will be treated as a primitive type at indirectly referring.
Not only in typeof, See #2423 .
it could be nice if literal types will be excluded from primitive type cleanly.

@vkurchatkin
Copy link
Contributor Author

@iamchenxin you are right, they are also not captured by polymorphic functions.

@ckknight
Copy link
Contributor

ckknight commented Dec 12, 2016

Here's an example of this in action:


Edit: the link I previously had now redirects to an unrelated site.

@vkurchatkin
Copy link
Contributor Author

In TypeScript behaviour depends on type of binding:

const x = 'X';
const y: typeof x = 'Y'; // error
let x = 'X';
const y: typeof x = 'Y'; // no error

@romulof
Copy link

romulof commented Nov 8, 2017

To put in another way:

const foo = 'foo'
const bar: typeof foo = 'bar'

Since foo is declared as constant it's not possible to assume that its type is the literal value?

@ahem
Copy link

ahem commented Nov 15, 2017

Yes! To me it also seems like it would be much nicer if typeof foo was 'foo', since that would provide more information, and still allow the foo constant to be used everywhere a string was expected. It would be very valuable to have a way to export constants, that other modules can then use for type-refinement.

@k
Copy link

k commented Nov 16, 2017

But what about typeof 'string'?

Or is flow's checking using typeof different then javascripts typeof?

@ahem
Copy link

ahem commented Nov 17, 2017

@k so flow's typeof is a flow operator that returns flow types. It only works in the flow domain, as in type declarations and annotations and so on. Javascripts typeof is an operator return strings with the name of the javascript type (more or less only 'string', 'number', 'boolean', 'object' or 'undefined').

So yes, they are completely different :-)

As for typeof "literal string" (flow's typeof), then I feel like that should also return the type "literal string". It would still be usable everywhere that a string type was expected, and it carries a lot more information

@mrkev
Copy link
Contributor

mrkev commented May 22, 2018

I dig Typescript's implementation making use of const.

@stomvi
Copy link

stomvi commented Sep 4, 2018

+1

When defining reusable consts as module exports, code that needs to be refined still has to compare some variable explicit with string literal. It should be refined using === typeof someConst which doesn't work for now.

gnprice added a commit to zulip/zulip-mobile that referenced this issue Jan 11, 2019
When Flow sees a definition like `const FOO = 'FOO'`, it knows for
some purposes that `FOO` is specifically `'FOO'`... but for others, it
apparently broadens it to `string`.  This is facebook/flow#2639.

One consequence is that when we say

  switch (action.type) {
    case NOT_FOO_LOL:
      // ... use `action` ...
      break;

    // ...
  }

with a `case` statement for a value of `type` that we've claimed is
*impossible*... Flow quietly just decides that `action` has type
`empty` and there's nothing to worry about.

Then if our code goes and passes that `action` value to *any function
at all*, Flow considers it valid.  After all, there are no values of
type `empty` (that's what `empty` is all about)... so it's true that
every value of type `empty` is a valid value of whatever type it is
that the function expected.

So, that's pretty unfortunate.  Fortunately, there is a workaround
that causes Flow to behave better here: duplicate the value into the
type, like `const FOO: 'FOO' = 'FOO'`.  (This trick due to @geraldyeo
and @fagerbua, in comments on facebook/flow#2377 -- thanks!)

Do that, then.

---

Made the edit with

  perl -i -0pe \
    's/export const ([A-Z_]+)\K =/: "$1" =/g' \
    src/actionConstants.js

and then manual fixup on the one exceptional case `REHYDRATE`,
which Flow kindly pointed out.

We had a whole bunch of type errors of the kind this hides; the bulk
of them were for Event*Action types, and their fixes went into the
parent commit.  This commit adds one more missing action type which a
number of reducers had type errors on, and a few other simple fixes.

PS: A quick tip for reading this diff: `git log -p --patience`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants