I have an object with conditional keys. I.e.:
const headers: RequestHeaders = {};
if (...) {
  headers.foo = 'foo';
}
if (...) {
  headers.bar = 'bar';
}
I'm new to TS and I expected this to work:
type RequestHeaders = {
  foo?: string,
  bar?: string,
};
However, I'm passing this to fetch and the type definition for fetch's headers is { [key: string]: string }. I'm getting:
Type 'RequestHeaders' is not assignable to type '{ [key: string]: string; }'.
  Property 'foo' is incompatible with index signature.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.
The only way I could get this to work is type RequestHeaders = { [key: string]: string };. Is there a way to limit the keys to a set of predefined strings?
The fetch API does not accept a headers object that has a key with an undefined value. Since each of your optional types can be either string | undefined, the compiler is rejecting them. 
Here is an approach that filters the headers to remove those with undefined values. Its type predicate (is) keeps the compiler happy. 
const buildHeaders = (requestHeaders: RequestHeaders): HeadersInit =>
  Object.entries(requestHeaders).filter(
    (entry): entry is [string, string] => entry[1] !== undefined
  );
const headers: RequestHeaders = {};
type RequestHeaders = {
  foo?: string; // optional
  bar?: string; // optional
  baz: string; // required!
};
fetch("Some Data", {
  headers: buildHeaders(headers)
});
The advantage of this approach is that it lets you limit the keys to a set of predefined strings while also letting you to specify whether each is required or optional.
The header data of fetch method must be type like
type HeadersInit = Headers | string[][] | Record<string, string>;
For your case, I think you will define headers type as a alias of Record<string, string>. For keys configuration (foo, bar), I will have a suggestion: Fixed header keys. Then, you will define all header keys to a type like:
type HeaderKeys = 'foo' | 'bar';
type RequestHeaders = Record<HeaderKeys, string>; // the same: type RequestHeaders = Record<'foo' | 'bar', string>;
const headers: RequestHeaders = {} as RequestHeaders; // force cast
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With