Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

In TypeScript's type system how can I check for freshness for typed parameters?

Building off of this question (no I do not have a grand scheme, I simply thought of this after getting my question answered.):

I have the following:

type LengthOfString<T extends string, L extends any[] = []> = 
  T extends `${string}${infer R}` 
    ? LengthOfString<R, [...L, string]>
    : L["length"]

type StringOfLength<T extends string, N extends number> = LengthOfString<T> extends N ? T : never

function isStringOfLength<T extends string, N extends number>(str: T, len: N): str is StringOfLength<T, N> {
  return str.length === len
}

function takeString<T extends string>(str: StringOfLength<T, 5>) {
  if (!isStringOfLength<T, 5>(str, 5)) throw Error("String length !== 5")
  console.log(str)
}

// Works! (fresh)
takeString("22222")

// As expected, errors (fresh)
takeString("2222")

let str = "22222"

// Errors -- how can I avoid this? (str isn't fresh)
takeString(str)

As shown in the code, I have a run-time type guard isStringOfLength that is meant to throw runtime errors for non-fresh variables. However, I am unsure about how to change my code to accept these non-fresh values, while type-checking against the fresh strings as shown with the first two calls.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

TS Playground

>Solution :

If we want to accept

  • valid literal string types

  • just the string type

but don’t want to accept

  • invalid literal string types

we can add an additional check to the function parameter.

function takeString<
  T extends string
>(str: string extends T ? T : StringOfLength<T, 5>) {
  if (!isStringOfLength<T, 5>(str, 5)) throw Error("String length !== 5")
  console.log(str)
}

We accept T as an argument to the function if string extends T. But if T is a string literal, the argument’s type must be StringOfLength<T, 5>. This might seem counter-intuitive as we check if string extends T before we even infer T but the compiler makes it work for us.

// Works! (fresh)
takeString("22222")

// As expected, errors (fresh)
takeString("2222")

let str = "22222"
// Does not error anymore
takeString(str)

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading