0

I have define a type like below

export type InputType = {
  configJsonUrl?: string;
  configJsObject?: DataType;
  rawData?: { [key: string]: string }; 
  action?: () => void;
};

export type DataType = {
  id: string;
  type: string;
  'product-configuration': { input: string };
};

For the InputType, even though all of the 3 values of optional(configJsonUrl, configJsObject & rawData) by themselves, st least one of these should be there in the React Component I am using this type in as shown in the examples below.

<ComponentX configJsonUrl='./data/info.json' />);
<ComponentX configJsObject={jsObject} />);
<ComponentX rawData={adhocData} />);

How to make one of these mandatory?

I am aware that I can make these mandatory probably like so

export type InputType = {
  input: string|DataType|{ [key: string]: string }
  action?: () => void;
};

However, I want them to have separate names as well (configJsonUrl, configJsObject & rawData) since I use these names to do some conditional rendering.

How to achieve this? Is it even possible in TS? Please guide.

Update Currently I am doing so in my react component

const FormGenerator = ({
  configJsonUrl,
  configJsObject,
  rawData,
}: InputType ): JSX.Element => {
  const [propForForm, setPropForForm] = useState<string>('dummy');
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    if (configJsonUrl) {      
          setPropForForm("URL");
          setLoading(false); // Stop loading
    } else if (rawData) {
          setPropForForm("adhoc object");
          setLoading(false); // Stop loading
   
    } else if (configJsObject) {
          setPropForForm("JS object");
          setLoading(false); // Stop loading      
    }
  }, [configJsonUrl, rawData, configJsObject]);

//rest of the rendering logic

Update2 Based on suggestions by Bergi , the InputType will become

export type InputType = {
  input: {configJsonUrl: string} | {configJsObject: DataType} | {rawData: {[key: string]: string}}
action?: () => void;
};

The react component will change to

const FormGenerator = ({input}: InputType )
//rest of the rendering logic

How to access (configJsonUrl, configJsObject & rawData) from the input?

moys
  • 7,747
  • 2
  • 11
  • 42
  • 1
    Are you looking for `type InputType = {configJsonUrl: string} | {configJsObject: DataType} | {rawData: {[key: string]: string}};`? – Bergi Aug 23 '23 at 01:46
  • @Bergi This might work. Now I have added the logic in my react component to the question for more clarity. How do use `InputType` in the react component? `input[configJsonUrl]` is throwing error `Cannot find name 'configJsonUrl'`. – moys Aug 23 '23 at 02:03
  • 1
    It's still [`input.configJsonUrl` or `input["configJsonUrl"]`](https://stackoverflow.com/q/4968406/1048572) as before with the optional properties. You can also use destructuring. Though for typescript not to complain, you probably first need to check `if ("configJsonUrl" in input)` to distinguish the three input types. – Bergi Aug 23 '23 at 02:35
  • Earlier the input to react compoent was something like `);` what would that be now? – moys Aug 23 '23 at 03:31
  • Nothing changes. Why would it? Do you want it to change? Does it no longer work? – Bergi Aug 23 '23 at 03:39
  • To keep the question simple, I had removed the other items in my `InputType` (for example a function). I have added that now to the question. So, my action input type would be , `type InputType ={ input: {configJsonUrl: string} | {configJsObject: DataType} | {rawData: {[key: string]: string}}, action?:() => void }.` with this structure, `)` is not working . I get the error `Type '{ configJsonUrl: string; }' is not assignable to type 'IntrinsicAttributes & InputType'.` – moys Aug 23 '23 at 03:48

1 Answers1

4

The simplest way is to expand your type into three types, each with the single required field and then make your props type be the union of those three distinct types:

interface InputFromUrl {
  configJsonUrl: string
}

interface InputFromObject {
  configJsObject: DataType
}

interface InputFromRawData {
  rawData: { [key: string]: string }
}

export type InputType = InputFromUrl | InputFromObject | InputFromRawData;

export type DataType = {
  id: string;
  type: string;
  'product-configuration': { input: string };
};

You can use InputType be checking which key is present in a variable of that type:

declare const props: InputType;

if ('configJsonUrl' in props) {
  props.configJsonUrl // now works
} else if ('configJsObject' in props) {
  props.configJsObject // now works
} else {
  props.rawData // now works
}
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • How to get hold of `configJsonUrl`, `configJsObject` & `rawData` from within `InputType`? I have added a bit of react component code (where this type is being used) to make things clear. – moys Aug 23 '23 at 02:05