Skip to content

Unexpectedly narrow implicit generator typeΒ #63578

Description

@badeball

πŸ”Ž Search Terms

implicit generator union type

πŸ•— Version & Regression Information

I tested this in playground using latest version of the 3rd, 4th, 5th and 6th version lines. The problem is present in all of them.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.3#code/PQKhCgAIUhJBbADgGwJYGNUBdJYJ6ICmkAzqgOYB2AhlgK4BOxARoQGYD2TkzyH6Aa0hN4HAG6EAJgC4oMOZDZ1K6LKg6VcDahIYlCAMUK1GhAMIALVMkkAKNsfpNL1mZCMnnVmwEppkAHFCSkJtLC4AHgAlOmRiAB9IABl+WnVNRJi4lxtIRIAhakFyBg5lSTzIAGUsQkRKgFEAD2okOJJKgBF+GoZUSnIu2moAFWpeBMgxiaiOAHdK6ezCZGQAGkgxDlRJDeUBSnnKAD4FBQQUDGxcAmIyKk9iajZahh4+QWFCUQkZM+hFMpVOktDpQvoPE5zN47A5Hjk3JDTAi-IFgqFaJEkV5XBstjs9pQDkdTgC5MBwEoVGoNDAsNpdBDHMiYfZmTibP5sdDXD5IABvKCQPCoFYVOFQhEAbnAQuAwEgUW+4mIWCsHV4-AEADohag2JA2fCYdqGLFCHzBZBrcLRTY6QzwYZ2YQsoQjZKTWa4j4ZdaAL7gQOUoE0ygOsF6Z2PN22b2Efxuy1CkVi4TmmV6g1x83aj5pDTJm22tPxvOpMN+yDB636w1l9AwpiUIs2zhvWzoDQkHCN1yQDgGhtN4Kt4up+2gxmu80Izsw31CgNCwPBqnA2lTp1uufxhGJ2cLgUpu0VPcwzO17Pn1za5hFAQlMqUSRjkuT+mR-SFYqlco57ITXvX9n1fKsa0gOsAJ5GxtRIdBgmoPoODfCdJAjacqgQmhkOghE4OwpD1EXZc11DdIMKdH9Hz-F9bGAx8uHKfxqKfcpWzQngH3IJiX0vSDswYnizRfct0ALFtj3HU8uOKXjJDEiTwNlK9DSE+S4NqRASDfdtDS7Sge1ILSBwNdSRIUns6h0qTi3fdCtyjGo6lsKzEBIm1A1I2V1zDSinMI3D4MQ5D-CwkLiNszjgpw9R+KgmKiI4RSw1QmTEuQlL0mUrNDQy9RNOs3SuH07scDc0zSECgq3Jsq07LQ-z9GcxBXK0jzl2XXLXOq5LCBaNpCDqpdFBKzsysgfrWhQYhByqiK+oGmbhrs+ymsIZppriWwpsGjrqxXIMfPIzdP0wrS2rqML2qi9KtPi7M3KywtbOtaKtOeygctUy7EG1SQenpfpyDSsV1u6dBemB37-sBvoBg8iCEo+yRhiWC1XrWxz9E6NHxm2p7UawUZ8YtZSyOpCjscICGoYGWwAchoGBn8WnmZB2600ZunyAew1ufZz7QdyAX4fIT7yeOynTsdKNceJ9GGbxiZWeVn1OZFtXCD5pWFdJoXMc4omSYmCXDu6430dNeYVrbMaDKM0oFjmy39ad23pLB6nZjmON5n2ryDopjdw29+Y-bmfx0Z9jiZKdnWnYN+q3rj+Yza6n7E4Q1YPdGjsHd7FZkEqrOi9zlOvbOp0zCLzsi4Dw7g786ma9WOvVij0nW+QWO02z5Adf7pORs4of80rRupZD9bNsGnalrifxZ+W3vcl2madfXuJh89teF8IdODu6reD+JiYAAljEkUJhYcquox9+ettP0nL+oa+GERlSBMNE-tTPuI+QOCSDwMVfOE0naVT-gAwgQCQFvgrh+WW+hH5Owbt5cA-VEBcBwPgIgkBWK0QqAAXlsuPdI-gUjiQntaAQhA8BzC4G4HsYsqw0HgAmYyrChTX3gn0RAYZ-AsOBlWWq-gmDvw0MgPA1QtIAG0AC6VYdhCPZjKf0MosE4JuPgswHB4AcMoDgUh9VyEaEoRWbKQpahNCwKo7hGjMFNGwQwXBtxIDyxNnESAJihRmMoBY6hVjrTu3EVfKRMjo7zEUeozRzjtF4OIGzMWPiyGWPMckdJX0hQcMkKgUYtwAD89iRFCgMrUIxJSBhVmvmgeA2BQhVN5kGOJLi3H4OXu0VJpismBKUtY6g5ASBhMkZQaRUxBkxKFHQhhTCmlsNaJw4R1SeFDXQPwwRXDSnWhgW-D+xSJkzHmFWGBcC8AjIBmMyJpMfZTNrMwtRLSnFtJ0cQbk3S-G9MyUEjQJzBnDK+KM8ZYxyB3MgMgagAw6CDKWY82h9DGEMAedw607DYUosgLw9ZqABEUK2Ss60fYbDNguRE9wLoERTMcVo1xrzyXGn7L4kJ5oDluirEJIhBzCGgVEb1A54VYocFic8hJ7i3QfOtP4vpNDcD-NJVciZoKlHTIRXM-FzTUWLPmasvhOLNnLI1ZAIlkgSWAsueMncMIwUqPVcKml7TiCWsZbZDloEuXcSIbyha-Lep2vibSxJ1ReoSvBV8qh-SdlyrNWSkFYKZmIuRdsyAaLtXWixRsvFBrRFaQBRI81MiWpgpPrm8JCrOlDWtYmlZ1L-UOtkXUENUrvkRsgPGtVWaVWzKRSMIp106gAGlVXdtuCc-qdjbU8LhsDA5ySk2uwmDOrWyiq3NJrS8wN6Nu6NrDVkqsYhqDIDoOi0pa7RX4KiQsJloafkBObTK-uJagXXImN3St2rT0BvcSC7dN7pXBOTVqid9z32tLPcQcNYZG39E4ZQOg8BWAMCrF2Q98BKAHNg-B0IfqXnX3QBC7gwQ4P1sQIOrtkge34PqgAVQAHL9powAeQAOo0dSQAIio0SQ4cxKBsbWEKMwDGaMjAaAADRGOxvRRix18aFAAQTMCMWAQn2NyZDrJ60DGqMjEEwAWQaOxhjdAsBdg4RpyAgmaMAClaOKeU6x0hbGpMACsTq8f44GIAA

πŸ’» Code

/**
 * Implicit type signature before block removed:
 * 
 * function traverseFeatureChild(featureChild: FeatureChild): Generator<Rule | Location | RuleChild | Background | Step | Examples | DocString | DataTable | TableRow | TableCell, void, unknown>
 * 
 * Implicit type signature after block removed:
 * 
 * function traverseFeatureChild(featureChild: FeatureChild): Generator<FeatureChild, void, unknown>
 * 
 */
function* traverseFeatureChild(featureChild: FeatureChild) {
  yield featureChild;

  // Remove this block.
  if (featureChild.rule) {
    yield* traverseFeatureRule(featureChild.rule);
  }
}

function* traverseFeatureRule(rule: Rule) {
  yield rule;

  if (rule.location) {
    yield rule.location;
  }

  if (rule.children) {
    for (const child of rule.children) {
      yield* traverseRuleChild(child);
    }
  }
}

function* traverseRuleChild(ruleChild: RuleChild) {
  yield ruleChild;

  if (ruleChild.background) {
    yield* traverseBackground(ruleChild.background);
  }

  if (ruleChild.scenario) {
    yield* traverseScenario(ruleChild.scenario);
  }
}

function* traverseBackground(backgorund: Background) {
  yield backgorund;

  if (backgorund.location) {
    yield backgorund.location;
  }

  if (backgorund.steps) {
    for (const step of backgorund.steps) {
      yield* traverseStep(step);
    }
  }
}

function* traverseScenario(scenario: Scenario) {
  yield scenario;

  if (scenario.location) {
    yield scenario.location;
  }

  if (scenario.steps) {
    for (const step of scenario.steps) {
      yield* traverseStep(step);
    }
  }

  if (scenario.examples) {
    for (const example of scenario.examples) {
      yield* traverseExample(example);
    }
  }
}

function* traverseStep(step: Step) {
  yield step;

  if (step.location) {
    yield step.location;
  }

  if (step.docString) {
    yield* traverseDocString(step.docString);
  }

  if (step.dataTable) {
    yield* traverseDataTable(step.dataTable);
  }
}

function* traverseDocString(docString: DocString) {
  yield docString;

  if (docString.location) {
    yield docString.location;
  }
}

function* traverseDataTable(dataTable: DataTable) {
  yield dataTable;

  if (dataTable.location) {
    yield dataTable.location;
  }

  if (dataTable.rows) {
    for (const row of dataTable.rows) {
      yield* traverseRow(row);
    }
  }
}

function* traverseRow(row: TableRow) {
  yield row;

  if (row.location) {
    yield row.location;
  }

  if (row.cells) {
    for (const cell of row.cells) {
      yield* traverseCell(cell);
    }
  }
}

function* traverseCell(cell: TableCell) {
  yield cell;

  if (cell.location) {
    yield cell.location;
  }
}

function* traverseExample(example: Examples) {
  yield example;

  if (example.location) {
    yield example.location;
  }

  if (example.tableHeader) {
    yield* traverseRow(example.tableHeader);
  }

  if (example.tableBody) {
    for (const row of example.tableBody) {
      yield* traverseRow(row);
    }
  }
}

export type Background = {
  location: Location;
  keyword: string;
  name: string;
  description: string;
  steps: readonly Step[];
  id: string;
};
export type Comment = {
  location: Location;
  text: string;
};
export type DataTable = {
  location: Location;
  rows: readonly TableRow[];
};
export type DocString = {
  location: Location;
  mediaType?: string;
  content: string;
  delimiter: string;
};
export type Examples = {
  location: Location;
  tags: readonly Tag[];
  keyword: string;
  name: string;
  description: string;
  tableHeader?: TableRow;
  tableBody: readonly TableRow[];
  id: string;
};
export type Feature = {
  location: Location;
  tags: readonly Tag[];
  language: string;
  keyword: string;
  name: string;
  description: string;
  children: readonly FeatureChild[];
};
export type FeatureChild = {
  rule?: Rule;
  background?: Background;
  scenario?: Scenario;
};
export type Rule = {
  location: Location;
  tags: readonly Tag[];
  keyword: string;
  name: string;
  description: string;
  children: readonly RuleChild[];
  id: string;
};
export type RuleChild = {
  background?: Background;
  scenario?: Scenario;
};
export type Scenario = {
  location: Location;
  tags: readonly Tag[];
  keyword: string;
  name: string;
  description: string;
  steps: readonly Step[];
  examples: readonly Examples[];
  id: string;
};
export type Step = {
  location: Location;
  keyword: string;
  keywordType?: StepKeywordType;
  text: string;
  docString?: DocString;
  dataTable?: DataTable;
  id: string;
};
export type TableCell = {
  location: Location;
  value: string;
};
export type TableRow = {
  location: Location;
  cells: readonly TableCell[];
  id: string;
};
export type Tag = {
  location: Location;
  name: string;
  id: string;
};
export type Location = {
  line: number;
  column?: number;
};
export declare enum StepKeywordType {
  UNKNOWN = "Unknown",
  CONTEXT = "Context",
  ACTION = "Action",
  OUTCOME = "Outcome",
  CONJUNCTION = "Conjunction",
}

πŸ™ Actual behavior

The implicit type signature of traverseFeatureChild() contains a return value of a generator that doesn't include FeatureChild, even though that is the first yielded element. If one comments out the if-block in said function, it suddenly appears in the type signature.

πŸ™‚ Expected behavior

For FeatureChild to be included in the type signature.

Additional information about the issue

The traverse code is part of @badeball/cypress-cucumber-preprocessor and the types are from @cucumber/messages. In v33.0.0 of @cucumber/messages, the exported types changed from classes to types, which is when this problem of mine started to appear.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions