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

Allow for optional index signatures (for the sake of exactOptionalPropertyTypes) #46969

Open
OliverJAsh opened this issue Dec 1, 2021 · 7 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@OliverJAsh
Copy link
Contributor

Bug Report

🔎 Search Terms

exactOptionalPropertyTypes strictoptionalProperties Partial index signature dictionary record

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about exactOptionalPropertyTypes

⏯ Playground Link

Playground link with relevant code

💻 Code

type MyRecord = { x: string; z: string };

const r: Partial<MyRecord> = {
    x: "y",
    // Error as expected ✅
    z: undefined,
};

type MyDict = { [key: string]: string };

const d: Partial<MyDict> = {
    x: "y",
    // Expected error but got none ❌
    z: undefined,
};

🙁 Actual behavior

See code comments above.

🙂 Expected behavior

See code comments above.


This was briefly discussed in #44524 and #44421 (comment).

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Dec 1, 2021
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.6.0 milestone Dec 1, 2021
@ahejlsberg
Copy link
Member

This is effectively a design limitation. We don't support the notion of optional index signatures (i.e. it isn't possible to specify a ? in the declaration of an index signature) and before --exactOptionalPropertyTypes there was really no reason to. By their nature index signatures represent optional properties, and all an optional index signature would do is add undefined to the type--which you could just do manually. However, with --exactOptionalPropertyTypes we now have two kinds of undefined, and the only way to get the missing-property kind is through the ? modifier on a property declaration. For this reason we may want to consider supporting the ? modifier on index signatures.

@sgvictorino
Copy link

sgvictorino commented May 1, 2022

Is there a way to detect that a type is indexed (but not mapped) as a workaround?

Update: I ended up with the following. If anyone stumbles upon this and has an idea for improvement please let me know:

type IsIndexed<T> = {
    [IndexType in string | number | symbol]: IndexType extends keyof T
        ? true
        : false;
}[keyof T];

interface Test {
    prop: string;
}
const value: IsIndexed<string> = false;
const object: IsIndexed<Test> = false;
const mapped: IsIndexed<Record<keyof Test, unknown>> = false;
const indexed: IsIndexed<Record<keyof Test | string, unknown>> = true;
const indexedWithoutString: IsIndexed<Record<number | symbol, unknown>> = true;

@jeremybparagon
Copy link

If we were able to use Object.values on a Partial type without getting undefined included in the array type, that would be pretty nice. I like to mark index signature values with the undefined possibility, but then when I use Object.values on the object, I have to filter out undefined values that aren't really there. For example (link):

// Use Partial since ranges might not be set for all keys
type RangeDictionary = Partial<{ [key: string]: { start: number, end: number }}>;

function getRangeEnds(rangeDictionary: RangeDictionary) {
  // TypeScript wants me to consider the possibility that range is undefined here
  return Object.values(rangeDictionary).map(range => range.end);
}

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature and removed Needs Investigation This issue needs a team member to investigate its status. Rescheduled This issue was previously scheduled to an earlier milestone labels Feb 2, 2023
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 5.1.0 milestone Feb 2, 2023
@RyanCavanaugh RyanCavanaugh changed the title exactOptionalPropertyTypes: Partial of index signature adds undefined Allow for optional index signatures (for the sake of exactOptionalPropertyTypes) Feb 2, 2023
@unional
Copy link
Contributor

unional commented Apr 29, 2023

Destructuring + Partial also doesn't work:

function foo(v: Partial<{ a: number }>) {}

function boo({ a }: Partial<{ a: number }>) {
	// Argument of type '{ a: number | undefined; }' is not assignable 
	// to parameter of type 'Partial<{ a: number; }>' with 'exactOptionalPropertyTypes: true'
	foo({ a })
}

playground

Should that be tracked here or should it be a different issue?

@Szorcu
Copy link

Szorcu commented Dec 4, 2023

I have encountered the same problem and cannot find a solution anywhere. Has anyone managed to solve it?

@GrantGryczan
Copy link

GrantGryczan commented Dec 5, 2023

This is NOT just for the sake of exactOptionalPropertyTypes! It would also make it safer to set properties on an object whose type has an index signature. Currently, TS often just lets you get and set unknown properties on types with [key: string]: ... | undefined, since that covers all unknown properties. For instance, after renaming or removing a field in an interface, I generally don't get TS errors signaling me to rename or remove the field where it's set elsewhere.

This can lead to bugs that I only discover at runtime. Being able to use [key: string]?: ... in an interface (just like [Key in string]?: ..., which can't be used in interfaces) would prevent this, with or without exactOptionalPropertyTypes. Currently I have no alternative if I want to use interfaces (which are enforced by TS's stylistic ESLint config which my project, now regrettably, uses).

@Lonli-Lokli
Copy link

I have a related proposal - is it possible to report with noUncheckedIndexAccess=true just the record (but not arrays)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

9 participants