Skip to content

Design Meeting Notes, 6/16/2021 #45103

@DanielRosenwasser

Description

@DanielRosenwasser

Generalized Index Signatures

#44604

  • Someone asked if they'd be allowed to write

    type FancyIdString = string & { __someTag: never };
    
    interface FancyIdContainer {
        [id: FancyIdString]: boolean;
        //   ^^^^^^^^^^^^^
        // This is an *intersection* with a primitive type.
    }
    • We figured: there's no reason to disallow it.
    • Reasoning: as long as you don't have a unit type or a generic, this should be permitted.
      • Unit types - we feel like we need to figure out how those tie into properties.
      • Generics - we don't have an entirely coherent model for what that means.
  • You can also have a union, provided that they consist of "infinite" primitive types, tagged intersections.

    // Works!
    interface Foo {
        [id: string | number | (symbol & { __fancyTag: never })]: boolean;
    }
  • Non-union enums are technically unbounded - you can assign any number to them - should we allow them?

    enum E {
        A = +0,
        B,
    }
    
    interface Foo {
        [id: E]: boolean;
    }
    • Kind of iffy.
    • We also don't like people to use non-union enums.
    • It is strange because you have the same "branding" issue between enums and tagged primitives.
      • Well, there are no branded literals.
      • But does the brand disappear in the base type?
  • How does keyof work on branded index signatures?

    type FancyIdString = string & { __someTag: never };
    
    interface FancyIdContainer {
        [id: FancyIdString]: boolean;
    }
    
    type FancyKeys = keyof FancyIdContainer; // FancyIdString
    
    type ReconstructedFancy = Record<FancyKeys, boolean>; ?
    • And also, how do we handle the interplay of branded index signatures and properties? Do we detect conflicts?
  • How does the following work?

    type BrandedProp1 = "prop" & { __brand__: void };
    type BrandedProp2 = "prop" & { __brand2__: void };
    
    type Q = Record<BrandedProp1 | BrandedProp2, number>;
    • This gives you just { prop: number }...right?
    • No.
    • Empty object.
    • Wat?
    • Okay, but then keyof is perfectly preserved as BrandedProp1 | BrandedProp2
    • The types "look" like they're working, but there's loss of fidelity in some places.
  • Can we just omit support for branded index signatures?

    • Want to intersect several different template string types.
    • Don't need to do this to ship in 4.4.
    • But highly desired.
  • Making this work with property names, and straightening out the inconsistencies mentioned above, could be tough.

    • There's a lot of internal complexity in the compiler around fetching names of properties
      • In-source names
      • Symbolic names
      • Literal-generated names
    • Lots of legitimate use-cases to keep in mind though, not always about working with types.

Preserving Imports

#44137 (comment)
#44619

  • We had this idea for importsNotUsedAsValues: preserve-exact as a thing that just says "don't touch the imports"
    • Is this the right flag?
    • Sounds like a jumble of words anyway.
    • That was not the right flag for this.
    • importsNotUsedAsValues is about the entire import statement.
  • Strawperson name: noErasingImportedNames
    • Under isolatedModules: false, TypeScript can use information across files to determine whether it needs to drop an import.
    • Under isolatedModules: true, TypeScript will always preserve an import, and detect/error if it's going to cause runtime issues.
  • Outside of isolatedModules, this has (useful) behavior that doesn't exactly do what the flag says it does.
    • It's more type-driven to some extent - but we already do this outside of isolatedModules.
  • What would we recommend to people?
    • Depends!
  • Want to understand the use-cases a bit better for Svelte and Vue

Renaming strictOptionalProperties

#44604

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design NotesNotes from our design meetings

    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