Skip to content

An intermediate named reference is required to infer the type of object field indexed by a literal type #38780

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
castarco opened this issue May 26, 2020 · 2 comments
Labels
Duplicate An existing issue was already created

Comments

@castarco
Copy link

castarco commented May 26, 2020

TypeScript Version(s): 3.9.3, next

Search Terms: type inference, indexed, union type, literal type

Code
This example works as intended, I'll comment the next one with a small tweak (although the temporal order was the reverse for me).

type FunnyType = {
  fieldA?: string[] | { [key: string]: string[] }
  fieldB?: string[] | { [key: string]: string[] }
}

function getNormalizedField(
  fieldName: 'fieldA' | 'fieldB',
  funnyObject: FunnyType,
  envName: string | undefined
) {
  let field: string[] = []
  const fieldOrigin = funnyObject[fieldName]
  if (fieldOrigin) {
    if (fieldOrigin instanceof Array) {
      field = fieldOrigin
    } else if (fieldOrigin instanceof Object) {
      if (!envName) {
        throw new Error('Environment name not provided, unable to select')
      }
      field = fieldOrigin[envName] ?? []
    }
  }
  return field
}

But this one, without the intermediate named object fieldOrigin, fails at line 14 (field = funnyObject[fieldName]), even if the assigned value is the same. When using an intermediate named object Typescript is able to narrow down its type, but for some reason is not able to do the same when the object reference is a little bit more "complicated" ( variable[index] ).

type FunnyType = {
  fieldA?: string[] | { [key: string]: string[] }
  fieldB?: string[] | { [key: string]: string[] }
}

function getNormalizedField(
  fieldName: 'fieldA' | 'fieldB',
  funnyObject: FunnyType,
  envName: string | undefined
) {
  let field: string[] = []
  if (funnyObject[fieldName]) {
    if (funnyObject[fieldName] instanceof Array) {
      field = funnyObject[fieldName]
    } else if (funnyObject[fieldName] instanceof Object) {
      if (!envName) {
        throw new Error('Environment name not provided, unable to select')
      }
      field = funnyObject[fieldName][envName] ?? []
    }
  }
  return field
}

Expected behavior:
Typescript should be able to narrow down the type of "simple" reference (with shape variable[index], where index belongs to a well identified finite set of literal values (I think the formal name is "literal type"?), and the types for each index of variable are also well known.

Actual behavior:
Typescript is not able to narrow down the type of such "simple" reference, and it needs some extra help, by creating an intermediate named reference.

Playground Link:
Playground Link

Related Issues:

@MartinJohns
Copy link
Contributor

Duplicate #10530. Type narrowing does not occur for indexed access forms e[k] where k is not a literal.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jun 4, 2020
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants