映射类型:根据同一对象的数组是否包含字符串文字,使 属性 成为必需项

Mapped types: Make property required based on whether array of same object contains string literal

是否可以使对象 属性 依赖于同一对象的数组是否包含字符串文字?

type Operator = "A" |  "B"
type SomeStruct = {
    operators: Operator[];
    someProp: string; // this should be required if operators include "A", optional if not
}

// desired result
const structWithA: SomeStruct = {
  operators: ["A", "B"],
  someProp: "" // correct, since operators contains "A", someProp is required
};

const structWithB: SomeStruct = {
  operators: ["B"],
  // currently errors, but desired outcome is that it should not error, since operators does not contain "A"
};

declare const structs: SomeStruct[];

structs.map(struct => {
  if(struct.operators.includes("A")) {
    // someProp is safely accessible
  }

  // since .includes has a narrow type signature, maybe another way to safely access someProp is needed 
})

通过将泛型应用于 SomeStruct(并使用 predicate 来区分其类型),您可以完成此操作:

TS Playground

type A = 'A';
type B = 'B';
type Operator = A | B;

type SomeStruct<O extends Operator> = { operators: O[] } & (
  Record<'someProp', string> extends infer T ?
    A extends O ? T : Partial<T>
    : unknown
);

const s1: SomeStruct<Operator> = { operators: ['B', 'A'], someProp: '' };
const s2: SomeStruct<Operator> = { operators: ['B', 'A'] }; // Error (2322)
const s3: SomeStruct<A> = { operators: ['A'], someProp: '' };
const s4: SomeStruct<A> = { operators: ['A'] }; // Error (2322)
const s5: SomeStruct<B> = { operators: ['B'] };
const s6: SomeStruct<B> = { operators: ['B'], someProp: '' };
const s7: SomeStruct<A> = { operators: [] }; // Error (2322)
const s8: SomeStruct<A> = { operators: [], someProp: '' };
const s9: SomeStruct<B> = { operators: [], someProp: '' };
const s10: SomeStruct<B> = { operators: [], someProp: '' };

declare const structs: (SomeStruct<Operator>)[];

function includesA (struct: SomeStruct<Operator>): struct is SomeStruct<A> {
  return struct.operators.includes('A');
}

structs.map(struct => {
  if(includesA(struct)) {
    struct.someProp; // string
  }
});