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

Add support for generic types on a index accessor #58258

Closed
6 tasks done
ruochenjia opened this issue Apr 19, 2024 · 4 comments
Closed
6 tasks done

Add support for generic types on a index accessor #58258

ruochenjia opened this issue Apr 19, 2024 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@ruochenjia
Copy link

πŸ” Search Terms

index accessor
generic index accessor

βœ… Viability Checklist

⭐ Suggestion

Allow type parameters to be used for an index accessor. This allows users to declare additional supporting methods within an interface that has a string index accessor with a value set to a non any or Function type, by using an overlay interface.

Example:

interface SuperRecord<Key extends keyof any, Value, Overlay = {}>{
	<E extends Key>[k: E]: E extends keyof Overlay ? Overlay[E]: Value;
};
interface K extends SuperRecord<string, string, { func1: () => void; }> {
	// Properties declared through the `SuperRecord` interface.
	// [k: string not "func1"]: string;
	// func1(): void;
}

let d: K = ...

d["anystringkey"] = "anystringvalue"; // properties not whitelisted in the overlay have the same behavior as a `Record<string, string>` interface type in this case.

d.func1(); // properties whitelisted by the overlay uses the value declared in the whitelist.

d.func1 = "A"; // error, as 'func1' exists in the overlay, so its corresponding type takes the priority.

πŸ“ƒ Motivating Example

This fixes #58256 and #17867, without using an intersection type. This would allow the type to be declared as an interface, which means it can be implemented by a class or extended by another interface without any errors caused by conflicting properties.

πŸ’» Use Cases

  1. What do you want to use this for?
    Various cases where an interface requires both an index accessor and other properties that could cause conflict in the type.
    Furthermore some types in lib.dom.d.ts can be improved, for example a string valued index accessor can be used for the Storage interface without affecting other supporting functions.

  2. What shortcomings exist with current approaches?
    It might slow down the type checking process.

  3. What workarounds are you using in the meantime?
    An intersection type like this:

type E = { [k: string]: string; } & { func1(): void };

which cannot be extended by an interface, or implemented by a class, due to property type conflicts.

@RyanCavanaugh
Copy link
Member

This seems like just an exact duplicate of #17867.

You can already write

type SuperRecord<Key extends keyof any, Value, Overlay = {}> = {
    [E in Key]: E extends keyof Overlay ? Overlay[E]: Value;
};

to get the generic behavior on an index signature's output type, nothing new is needed.

@ruochenjia
Copy link
Author

@RyanCavanaugh Logically the method you suggested should behave exactly the same as the one I mentioned above. However when I tested with the code below, only the key and value part works correctly, the overlay does not. Invoking a function on the overlay produces an error message:

This expression is not callable. Type 'String' has no call signatures.

The code I used:

type SuperRecord<Key extends keyof any, Value, Overlay = {}> = {
	[E in Key]: E extends keyof Overlay ? Overlay[E]: Value;
};
interface K extends SuperRecord<string, string, { func1(): void; }> {
}
let k: K = ...
k["str"] = "val"; // works fine
k.func1(); // error
k.toString(); // works fine

@RyanCavanaugh
Copy link
Member

Right, you'd still need rest signatures. But K would be declared as this:

type O = { func1(): void; };
interface K extends SuperRecord<keyof O, string, O> {
  // TODO: Be able to uncomment.
  // [...rest: string]: string
}

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Apr 22, 2024
@typescript-bot
Copy link
Collaborator

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

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Apr 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants