HelloWorld
HelloWorld

Reputation: 617

Is it possible to contrain inferred generics in typescript?

Take the following example:

type A<Keys extends string, Prefix extends string> = 
  {[K in Keys]: `${Prefix}-${K}`}

function noGood<K extends string, Prefix extends string>(
  obj: A<K, Prefix>, prefix: Prefix
) {
// ...
}

const test: A<'one' | 'two', 'prefix'> = {"one": 'prefix-one', "two": 'prefix-two'}

noGood(test, 'desired to only be "prefix", but can be any string...')

You can see that the generic types of noGood are inferred from the first param (obj). Then, when you provide a string to the second param (prefix)

Obviously you could type out your call to noGood and it would disallow that random string for the second parameter, but ideally you wouldn't have to.

Edit: The question was not clear.

To clarify, the problem is that when you infer the arguments to noGood, the Prefix inferrence does not work as desired (but not necessarily unexpected) since it would be ideal to have the Prefix constrained to the generic Prefix of obj (the first argument).

In this example, Prefix (generic) is literally 'prefix' and it would be desired to only allow that for the noGood second argument that is inferred. However, when you call noGood(obj, 'diff-prefix') the Prefix generic becomes 'prefix' | 'diff-prefix' and ideally it would be constrained to the inferred type from obj rather than the inferred type from the union of the first and the second argument.

Hope that clears things up. Can definitely see how it was unclear.

Upvotes: 0

Views: 45

Answers (2)

jered
jered

Reputation: 11581

@HelloWorld per your comment on my other answer, if you want the parameter prefix to only be a value at one of the keys of the object, it is still relatively easy with some tweaks:

const foo = {"one": 'prefix-one' as const, "two": 'prefix-two' as const}

function noGood<O extends {}>(
  obj: O, prefix: O[keyof O]
) {
// ...
}

noGood(foo, 'prefix-one'); // sure ok
noGood(foo, 'crabapple'); // error!

Note the use of as const when defining the values inside of foo. This is important because it tells TypeScript that those are values will not change and can be treated as a constant, explicit type and not just any old string. Otherwise, noGood(foo, 'crabapple') would not give an error because TypeScript normally would infer the values at each key of foo as a string and not "prefix-one" | "prefix-two".

If this still doesn't meet your use case, please update your question with more specific details and requirements.

Upvotes: 1

jered
jered

Reputation: 11581

This is trivial at a basic level if you don't care too much about constraining the object parameter of "noGood()". Use generics to enforce that the parameter prefix is a key of the object parameter obj (the word "prefix" seems like a misnomer here since you seem to only care about the keys of obj but I digress):

function noGood<O extends{}>(
  obj: O, prefix: keyof O
) {
// ...
}

There is not really a way that I know of to extract the type information for "Prefix" from your type A because that generic type information is not retained by the object after you instantiate it -- there is just no way to enforce that the object passed in as obj is definitely an instance of your type A.

Upvotes: 0

Related Questions