Skip to content

Type elimination and CFA/Type Guards #11557

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

Closed
normalser opened this issue Oct 12, 2016 · 3 comments
Closed

Type elimination and CFA/Type Guards #11557

normalser opened this issue Oct 12, 2016 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@normalser
Copy link

normalser commented Oct 12, 2016

It probably was discussed in the past - but couldn't find.

  1. Could TS not eliminate types and information about original type?
type EncryptedToken = 'TokenA' | 'TokenB' | string 

function generateToken():EncryptedToken { ... }

// In some other module:
const token = generateToken() {} // Type: string

so TS (even if its 'working as intended' from compiler point of view) not only eliminated important information for developer that generateToken function can return 'TokenA' | 'TokenB' | string but also removed original type name EncryptedToken which could be also important for developer to understand the code - leaving user with just string

so

1a: Could TS keep the type: 'TokenA' | 'TokenB' | string as 'TokenA' | 'TokenB' | string and not downgrade to string?

Theoretically equivalent (from user point of view) code:

enum Test { a, b, c }
type B = Test.a | Test.B | number // Type: Test.a | Test.B | number

retains the type

but

type C = 0 | 1 | number // Type: number

does not

1b: Could TS show the original type as well?

const token = generateToken() {}

Instead of showing type as string could it show:

type: EncryptedToken or
type: EncryptedToken = string (if 1a is not possible) or
type: EncryptedToken = 'TokenA' | 'TokenB' | string (if 1a possible)


Now because of 1a the following code will not work:

type A = 0 | 1 | number
function test(a:0|1) {}
let a:A
if (a == 0 || a == 1) {
  test(a) // Error: number is not assignable to 0 | 1
}

But this code will work:
EDIT: This code works because enum is pretty much treated as number (#11559):

const enum Enum { a0, a1, a2 }
type A = Enum.a0 | Enum.a1 | number
function test(a: Enum.a0|Enum.a1) {}
let a:A
if (a == 0 || a == 1) {
  test(a)
}

Problem is that I want to use the same pattern for strings - but there are no enum strings (yet #1206 (comment))

@ahejlsberg
Copy link
Member

I guess I'm not understanding what additional information is conveyed in a type "foo" | "bar" | string vs. just string. Either way, any string is a possible value, and "foo" | "bar" | string does not in any way provide more type safety than string.

@normalser
Copy link
Author

normalser commented Oct 12, 2016

@ahejlsberg

Here is an example - I know that I could write that piece of code in different way (or to use as) to avoid errors) - it's just an example:

// Just an alias
type UserGuid = string
type FindBy = 'cost' | 'size' | UserId

function getUserInputFromCommandLine():FindBy {
  console.log("Write: cost | size | {UserGuid}")
  return 'cost' // user input taken from command line
}

const by = getUserInputFromCommandLine()

function findBySpecificKind(kind: 'cost' | 'size') {

}

function findByUserId(userId: UserGuid) {

}

if (by == 'cost' || by == 'size') {
  findBySpecificKind(by) // ERROR - string not assignable to 'cost' | 'size'
}
else {
  findByUserId(by)
}

So I was hoping to improve user experience in 2 ways:

  1. Even if for compiler: "foo" | "bar" | string is the same as string - for user seeing that by or getUserInputFromCommandLine() returns type 'cost' | 'size' | UserGuid gives much much more information - than the current string. So kind of like self-documentation of the code
  2. If 'foo' | 'bar' | string wouldn't be simplified to string then I guess the following would work: (kind of like 'foo' | 'bar' gets priority over string)
if (by == 'cost' || by == 'size') {
  findBySpecificKind(by) // ERROR - string not assignable to 'cost' | 'size', but CFA could figure out that it matched string literals in (non simplified) type definition
}

But if you feel like this makes no sense - feel free to close it

@ahejlsberg
Copy link
Member

Ok, I think we should investigate is having equality comparison operations narrow from type string. For example:

const by: string = ...
if (by == 'cost' || by == 'size') {
  findBySpecificKind(by);  // by should narrow to "cost" | "size" here
}

This was already proposed in #11306, so I will close this one as a duplicate.

@ahejlsberg ahejlsberg added the Duplicate An existing issue was already created label Oct 12, 2016
@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
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants