0

I want to do a compile-time assertion on the following code:

interface FormFields {
  [K: string]: string | boolean | number;
}

function FormTextInput<
  FieldName extends keyof Fields,
  Fields extends FormFields
>(fieldName: FieldName) { ... }

// should throw compile-time error:
FormTextInput<'someNumberField', { someNumberField: number }>('someNumberField')

// should NOT throw error:
FormTextInput<'someStringField', { someStringField: string }>('someStringField')

so that FormTextInput always throws an error if FieldName refers to a non-string value of Fields

Is it possible do to this at compile-time with Typescript? I have seen some docs on asserts ( https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions ) but it doesn't seem it was meant to be used for this scenario

1 Answer 1

1

You can define helper utility which picks keys with string value from source type:

type PickKeysWithStringValue<T> =
  { [P in keyof T]: T[P] extends string ? P : never }[keyof T];

type Test = PickKeysWithStringValue<{ foo: string, bar: number }> // results in "foo"

function FormTextInput<Fields extends FormFields>(fieldName: PickKeysWithStringValue<Fields>) {  }

// throws compile-time error:
FormTextInput<{ someNumberField: number }>('someNumberField')

// doesn't throw error:
FormTextInput<{ someStringField: string }>('someStringField')

Playground

Sign up to request clarification or add additional context in comments.

5 Comments

works great! But question: Is there a way to improve the error message? it just says Type '"someNumberField"' does not satisfy the constraint '"someStringField"'
Also I am sure this came up before, but if I change the order of the generics in the function to <Fields, FieldName> why can't TS infer the type of the second generic from the arguments of the function. Would be nice to not need to specify the fieldName twice
Why do you need generic type parameter for field name?
I am doing some magic so the user can only pass fieldNames that match a certain type in Fields. To do that I need it as a generic
Can you add an example on playground?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.