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

Index signature alternative #32652

Open
4 tasks done
Akxe opened this issue Aug 1, 2019 · 2 comments
Open
4 tasks done

Index signature alternative #32652

Akxe opened this issue Aug 1, 2019 · 2 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

@Akxe
Copy link

Akxe commented Aug 1, 2019

Search Terms

  • Index signature alternative
  • Index signature

Suggestion

An alternative of Index signature would be beneficial when you want to enforce, that all properties of an object must follow given pattern, without the need of [key: string | number]: T1.

My suggestion is to enable the use of ? for index signatures to mark them as type guards only.

type posParams = {
  [name?: string]: UrlSegment;
};

Use Cases

Whenever there is a need for an object with unknown keys, but known value types this would come in handy. A lot of people trying to implement some sort of callback that requires the index signature fails.The enormous number of results if one googles Index signature is missing in type confirms this

I personally tend to confront this problem regularly when working with frameworks..

Examples

For this example, I will use Angular, but this is applicable to any typescript code.

type UrlMatchResult = {
  consumed: UrlSegment[];
  posParams?: {
    [name?: string]: UrlSegment;
  };
};

When one would be to implement it with only specific posParams it would only check for the type of the object keys and values.

export interface IUrlParameters {
	baseFolder: UrlSegment;
	currentFolder?: UrlSegment;
}

export function folderMatcher(segments: UrlSegment[]): UrlMatchResult {
  const posParams: IUrlParameters  = {
    currentFolder: segments[0],
  };

  return {
    consumed: segments,
    posParams,
  }
}

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • [-] This feature would agree with the rest of TypeScript's Design Goals.
@RyanCavanaugh RyanCavanaugh added 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 labels Aug 1, 2019
@jcalz
Copy link
Contributor

jcalz commented Aug 2, 2019

Related to #15300

I've often wanted things like this too, but to my knowledge the problem is that the compiler doesn't enforce that only known properties exist on an object. Without exact types there's no way to prevent something like the following:

function folderMatcher(segments: UrlSegment[]): UrlMatchResult {
  const posParamsWithRaisins = {
    baseFolder: segments[1],
    currentFolder: segments[0],
    raisins: true // not a UrlSegment
  };

  const posParams: IUrlParameters = posParamsWithRaisins; // not an error

  return {
    consumed: segments,
    posParams // should this work, or error?
  };
}

The assignment of posParamsWithRaisins to a variable annotated as IUrlParameters is not an error; object types in TypeScript are extendable/open and not exact. If there were a "type guard only" index signature as you want, the above return statement could not raise an error because the compiler has forgotten about the raisins property entirely. So even if all known properties conform to the index signature, it can't safely promote that object to an index-signature version. That's why the error is there. (It's considered unsafe to promote interface-typed objects, ❝safer❞ to promote type-alias-typed objects, and safe to promote object literals.)


Of course, the error goes away when you do this:

function folderMatcher(segments: UrlSegment[]): UrlMatchResult {
  const posParams = {
    baseFolder: segments[1],
    currentFolder: segments[0]
  };

  return {
    consumed: segments,
    posParams
  };
}

Why doesn't that work for you? If it's that you need to ensure that posParams is an IUrlParameters, you can get that guarantee without widening to the interface, like this:

const asIUrlParameters = <T extends IUrlParameters>(x: T) => x;

function folderMatcher(segments: UrlSegment[]): UrlMatchResult {
  const posParams = asIUrlParameters({
    baseFolder: segments[1],
    currentFolder: segments[0]
  });

  return {
    consumed: segments,
    posParams
  };
}

@Akxe
Copy link
Author

Akxe commented Aug 5, 2019

The proposition is to have a new kind of Index signature, that will only enforce all indexes to be of a type.

interface IOnlyStrings {
	[index?: string]: string;
}

interface baz {
	foo: string,
	baz: string,
}

interface resin {
	foo: string,
	resin: boolean,
}

const V1: IOnlyStrings = {
	foo: 'foo',
	baz: 'baz',
} as baz;
const V2: IOnlyStrings = {};
const V3: IOnlyStrings = {
	foo: 'foo',
	resin: true, // Error: All values must be of type 'string'
} as resin;

Why doesn't that work for you? If it's that you need to ensure that posParams is an IUrlParameters, you can get that guarantee without widening to the interface, like this:

The point here is no that there would not be any way around it, but to address this very common problem that confuses many peoples. Using this (<T extends IUrlParameters>(x: T) => x) pattern didn't come to my mind and is nice that it solves it, but it seems wrong to define function just as "type hack".

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

3 participants