1

So I'm stuck on a (confusing for me) situation, any help would be greatly appreciated:

I have some big objects with different types of properties. I'm using generics and iterating through the fields to render inputs based on the field's type. Ex text field for string, checkbox for bool, and so on.

Some of the fields could be null, so I can't just use typeof on the actual value, which means I have to create a type schema for the JS, which is feeling super redundant and not very DRY, just to create the inputs ex:

interface Data {
  foo: string
  bar: number
  baz: Date
}

const dataTypes = {
  foo: "string",
  bar: "number",
  baz: "Date"
}

It's a long shot, but I guess my question is: can I use the Typescript type in the JavaScript logic? Ex:

if ( typescript-typeof val == "boolean | null" ) drawCheckbox()

Thank you for any help!

=== EDIT === Example of when JS doesn't know the type:

interface Obj {
  foo: string | null
  bar: number | null
  baz: Date | null
}

const obj: Obj = {
  foo: "abc",
  bar: null,
  baz: new Date()
}

function drawInput(val: Obj[keyof Obj]) {
  switch (typeof val) {
    case "string": drawTextField()
    case "boolean": drawCheckbox()
    case "object": // originally a string, boolean, or Date?
  }
}

=== EDIT #2 only way I know how to make it work--- with redundant code ===

interface Obj {
  foo: string | null,
  bar: boolean | null,
  baz: Date | null
}

const objTypes: {[key in keyof Obj]: string} = {
  foo: "string",
  bar: "boolean",
  baz: "Date"
}

const obj: Obj = {
  foo: "abc",
  bar: null,
  baz: new Date()
}

function drawInput <K extends keyof Obj>(key: K, val: Obj[K]) {
  switch (objTypes[key]) {
    case "string": drawTextField(val)
    case "boolean": drawCheckbox(val)
    case "Date": drawDatePicker(val)
    case "object": // will never happen now
  }
}
2
  • 1
    typeof value === 'boolean' || value === null? What's so special about the Typescript type? And no, they only exist at compile time. Commented May 15, 2021 at 20:41
  • 1
    You can use typeof in javascript Commented May 15, 2021 at 20:41

2 Answers 2

2

The typeof keyword isn't specific to TypeScript. You can use it in JavaScript just as well.

Note: if val is null, typeof val will return "object".

if (typeof val === "boolean") drawCheckbox()

You can't however make types in JavaScript, like you would in TypeScript.

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

8 Comments

Hi MrCoding thanks for responding. I guess I'm still misunderstanding, how does JavaScript know if a null object property is supposed to be a string or a boolean? I'll try to add an example
You can actually use typeof on the variable even if it is null. It will just not return boolean for example but rather object. So it doesn't really know that, but that's why you're checking for null as well
Maybe I'm missing something, but If I use typeof on the value, and it returns null, I won't know what type of input to render
Just now I got what you are trying to do, and I have a disappointing answer. It's no possible with JS as long as you already have an object with all the values assigned to it
You could initialize the object with some placeholder values. So you could use a class instead of interfaces.
|
1

Does this pattern address your case?

It ensures you only assign names to types in one place, but allows them to drive javascript AND typescript logic in the ways you've requested.

type Form = Readonly<{
  [k in string]:"string"|"number"|"Date"
}>

type ValidationType<F extends Form> = Partial<{
  [K in keyof F]: F[K] extends "string"? string : F[K] extends "number" ? number : F[K] extends "Date" ? Date : never
}>

//DRY happens here
const myForm = {
  foo: "string",
  bar: "number",
  baz: "Date"
} as const;

//autocompletion happens here
const myRecord:ValidationType<typeof myForm> = {

}

function renderControls<F extends Form>(form:F, record:ValidationType<F>){
  for(const [key,value] of Object.entries(form)){
    if(form[key] === "string") { 
      renderString(record, key);
    }
    if(form[key] === "Date") {
      renderDate(record, key);
    }
  }
}

3 Comments

Wow holy cow you got a JS object to behave like a TS interface! This is excellent. It's unconventional but exactly what I was looking for. Thank you for your help, I wish I could upvote it more than once.
The only sad thing is that myForm cannot be typed like const myform:Form as that broadens the type which will otherwise be inferred by typescript. There's a workaround like stackoverflow.com/questions/67366450/… if you want the compiler to tell you that you put e.g. "date" instead of "Date"
Actually as long as it's passed into renderControls you'd get a compiler error anyway as it would choke and tell you it's not a Form.

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.