Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript lowercase string

Is there a way to make sure that TS will scream if it receives something else from lowercase string for fieldName?

type Props = {
  onEdit: (data: Record<string, string | number>) => void;
  title: string;
  fields: {
    fieldName: string;
    fieldValue?: string | number;
   }
};
like image 793
Tasos Tsournos Avatar asked Feb 15 '26 04:02

Tasos Tsournos


2 Answers

It's simple enough to create a utility to enforce that string literals are lowercase using the type utility Lowercase<StringType>:

TS Playground

type EnforceLowerCase<T extends string> = Lowercase<T> extends T ?
  string extends T ? never : T
  : never;
  
type Str1 = EnforceLowerCase<'Hello'>; // never
type Str2 = EnforceLowerCase<'hello'>; // "hello"

However, using it in your scenario would be very convoluted, because it would require using only string literals (which TS doesn't infer on object property values).


Instead, it's better to ensure conformance of the expectation at runtime. Here's an example using a pure function which doesn't mutate the input object:

TS Playground

function ensureLowerCaseFieldName (props: Props): Props {
  return {...props, fields: {
    ...props.fields,
    fieldName: props.fields.fieldName.toLocaleLowerCase(),
  }};
}

function doSomethingWithProps (props: Props) {
 props = ensureLowerCaseFieldName(props);
 console.log(props);
}

const props: Props = {
  onEdit: data => {},
  title: 'title',
  fields: {fieldName: 'Hello'},
};

doSomethingWithProps(props); // {..., fields: { fieldName: "hello" } }
like image 96
jsejcksn Avatar answered Feb 16 '26 18:02

jsejcksn


As of TypeScript 4.8 there is now support for using the intrinsic string manipulation types with wide types like string. This was implemented in microsoft/TypeScript#47050 but not mentioned in the TS 4.8 release notes.

So now, Lowercase<string> only corresponds to lowercase strings (strings which remain unchanged after .toLowerCase()), whereas in TypeScript 4.7 and below it could match any string:

type Props = {
    onEdit: (data: Record<string, string | number>) => void;
    title: string;
    fields: {
        fieldName: Lowercase<string>;
        fieldValue?: string | number;
    }
};

const p: Props = {
    onEdit() { },
    title: "ABC",
    fields: {
        fieldName: "okay"  // okay      
    }
}
p.fields.fieldName = "Oops" // error! Type 'string' is not assignable to type 'Lowercase<string>'
p.fields.fieldName = "123" // okay, Lowercase<"123"> is "123"

Playground link to code

like image 37
jcalz Avatar answered Feb 16 '26 17:02

jcalz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!