awesome-typescript icon indicating copy to clipboard operation
awesome-typescript copied to clipboard

「重学TS 2.0 」TS 练习题第二十二题

Open semlinker opened this issue 3 years ago • 28 comments

实现一个 RequireAtLeastOne 工具类型,它将创建至少含有一个给定 Keys 的类型,其余的 Keys 保持原样。具体的使用示例如下所示:

type Responder = {
   text?: () => string;
   json?: () => string;
   secure?: boolean;
};

type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
> = // 你的实现代码

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    json: () => '{"message": "ok"}',
    secure: true
};

请在下面评论你的答案。

semlinker avatar Sep 20 '21 12:09 semlinker

type Responder = {
   text?: () => string;
   json?: () => string;
   secure?: boolean;
};

type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
> = KeysType extends unknown ? Responder & {[K in KeysType]-?: ObjectType[K]}: never;// 你的实现代码

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    json: () => '{"message": "ok"}',
    secure: true
};

// @ts-expect-error 因为没有'text'和'json'中的任何一个,报错
const responder2: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    secure: true
};

额外增加了一个测试用例。

suica avatar Sep 20 '21 15:09 suica

type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
};

type RequireAtLeastOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = {
  [K in keyof ObjectType]: K extends KeysType ? Responder & Required<Pick<Responder, K>> : never;
}[keyof ObjectType];

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, "text" | "json"> = {
  json: () => '{"message": "ok"}',
  secure: true,
};

zhaoxiongfei avatar Sep 20 '21 16:09 zhaoxiongfei

type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
};

type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
> = KeysType extends keyof ObjectType ? 
  ObjectType & Required<Pick<ObjectType, KeysType>>: 
  never;


// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    json: () => '{"message": "ok"}',
    secure: true
};

const responder2: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    secure: true
};

const responder3: RequireAtLeastOne<Responder, 'text' | 'json'> = {
};

pingan8787 avatar Sep 21 '21 15:09 pingan8787

type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
  ee: number
};
type SetRequired<T, K extends keyof T> = Required<Pick<T, K>>;
type GetAtleast<ObjectType, KeysType extends keyof ObjectType> =
 KeysType extends infer A | infer B ?  
 Required<Pick<ObjectType, KeysType>> | SetRequired<ObjectType, A> | SetRequired<ObjectType, B>: 
 never;
type RequireAtLeastOne<
   ObjectType,
   KeysType extends keyof ObjectType = keyof ObjectType,
> = GetAtleast<ObjectType, KeysType> & Omit<ObjectType, KeysType>;

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
   // json: () => '{"message": "ok"}',
   text: () => '{"message": "ok"}',
   secure: true,
   ee: 2
};

mingzhans avatar Sep 22 '21 13:09 mingzhans

  type Responder = {
    text?: () => string
    json?: () => string
    secure?: boolean
  }

  type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
    > = KeysType extends KeysType ? Required<Pick<ObjectType, KeysType>> & Omit<ObjectType, KeysType> : never

  type T1 = RequireAtLeastOne<Responder, 'text' | 'json'>

  // 表示当前类型至少包含 'text' 或 'json' 键
  const responder: T1 = {
    json: () => '{"message": "ok"}',
    secure: true
  }
  const responder2: T1 = {
    text: () => '{"message": "ok"}',
    secure: true
  }
  const responder3: T1 = {
    json: () => '{"message": "ok"}',
    text: () => '{"message": "ok"}',
    secure: true
  }
  const responder4: T1 = {
    json: () => '{"message": "ok"}',
  }
  const responder5: T1 = {
    text: () => '{"message": "ok"}',
  }
  const responder6: T1 = {  // Error
    secure: true
  }

ln0y avatar Sep 24 '21 02:09 ln0y

// 实现一个 RequireAtLeastOne 工具类型,它将创建至少含有一个给定 Keys 的类型,其余的 Keys 保持原样。具体的使用示例如下所示:

type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
};

type RequireAtLeastOne<
   ObjectType,
   KeysType extends keyof ObjectType = keyof ObjectType,
> = KeysType extends any
  ? Omit<ObjectType, KeysType> & Required<Pick<ObjectType, KeysType>>
  : never

思路: 这里利用了联合类型作为泛型是 extends 会分发处理的特性,之后将去掉某个属性的类型与只有某个属性,且必填的类型做交叉合并

zhaoxiongfei avatar Oct 02 '21 09:10 zhaoxiongfei

type Responder = {
    text?: () => string;
    json?: () => string;
    secure?: boolean;
};

type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
    > = KeysType extends keyof ObjectType ? ObjectType & Required<Pick<ObjectType, KeysType>> : never

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    json: () => '{"message": "ok"}',
    secure: true
};

Mrlgm avatar Oct 04 '21 15:10 Mrlgm

type RequireAtLeastOne<
  ObjectType,
  KeysType extends keyof ObjectType = keyof ObjectType
> = KeysType extends string
  ? { [P in KeysType]: ObjectType[P] } & Omit<ObjectType, KeysType>
  : never;

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder> = {
  json: () => '{"message": "ok"}',
  secure: true
};

Gengar-666 avatar Oct 15 '21 06:10 Gengar-666

type Responder = {
   text?: () => string;
   json?: () => string;
   secure?: boolean;
};

type RequireAtLeastOne<
    ObjectType,
    KeysType extends keyof ObjectType = keyof ObjectType,
> = Omit<ObjectType, KeysType> & {
    [k in KeysType]: Required<Pick<ObjectType, k>>
}[KeysType]// 你的实现代码

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
    json: () => '{"message": "ok"}',
    secure: true
};

Flavour86 avatar Nov 04 '21 02:11 Flavour86

哪位大佬能解释下 KeysType extends keyof ObjectType = keyof ObjectType 这句话是什么意思?

yubaoquan avatar Nov 08 '21 03:11 yubaoquan

// 21
type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
};

type RequireAtLeastOne<
   ObjectType,
   KeysType extends keyof ObjectType = keyof ObjectType,
> = { [P in KeysType]-?: ObjectType[P] } & ObjectType;

// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'secure'> = {
   json: () => '{"message": "ok"}',
   secure: true
};

Ljp10086 avatar Jan 04 '22 04:01 Ljp10086

联合类型分散后再进行联合

type Responder = {
  text?: () => string;
  json?: () => string;
  secure?: boolean;
};

type RequireAtLeastOne<
   ObjectType,
   KeysType extends keyof ObjectType = keyof ObjectType,
> =
  KeysType extends KeysType
    ? Required<Pick<ObjectType, KeysType>> & Omit<ObjectType, KeysType>
    : never

type X = RequireAtLeastOne<Responder, 'text' | 'json'>

waleiwalei avatar Mar 16 '22 02:03 waleiwalei

哪位大佬能解释下 KeysType extends keyof ObjectType = keyof ObjectType 这句话是什么意思?

这不是对传入的第二个类型参数做一个泛型约束吗,表示只能传第一个类型的key组成的联合类型的父类型,也就是你传入的第二个类型参数,必须是联合类型,且必须由第一个类型的key组成,默认为全部的key

xq52301nzdm avatar May 07 '22 03:05 xq52301nzdm

平安已经收到你的邮件啦

pingan8787 avatar May 07 '22 03:05 pingan8787

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar May 07 '22 03:05 xq52301nzdm

[K in KeysType]-?

"-" 这个符号是什么意思哦

a635273670 avatar Aug 25 '22 03:08 a635273670

[K in KeysType]-?

"-"这个符号是什么英文哦

就是 减去可选,变成必选

charles-lpd avatar Sep 07 '22 04:09 charles-lpd

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Oct 11 '22 07:10 xq52301nzdm

为什么看上去不对,答案却是对的? image 分开操作 意思明明是 text 和 json 都必须有???

zhengyimeng avatar Nov 11 '22 03:11 zhengyimeng

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Nov 11 '22 03:11 xq52301nzdm

type RequireAtLeastOne<ObjectType, KeysType extends keyof ObjectType = keyof ObjectType> = Omit<ObjectType, KeysType> &
  (KeysType extends keyof ObjectType ? Required<Pick<ObjectType, KeysType>> : never);

zhixiaotong avatar Feb 03 '23 02:02 zhixiaotong

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Feb 03 '23 02:02 xq52301nzdm

type Responder = {
  text?: () => string
  json: () => string
  secure?: boolean
  password: string
}
type Merge<Obj extends Record<string, any>> = Obj extends any
  ? {
      [Key in keyof Obj]: Obj[Key]
    }
  : never
type RequireAtLeastOne<T extends Record<string, any>, K extends keyof T = keyof T> = Exclude<
  {
    [Key in K]: Merge<
      {
        [Key2 in Key]: Exclude<T[Key2], undefined>
      } & {
        [Key3 in Exclude<keyof T, K>]: T[Key3]
      }
    >
  }[K],
  undefined
>
type TTestRequireAtLeastOne = RequireAtLeastOne<Responder, 'text' | 'json'>
const responder: TTestRequireAtLeastOne = {
  json: () => '{"message": "ok"}',
  secure: true,
  password: '',
}

yitjhy avatar Feb 22 '23 10:02 yitjhy

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Feb 22 '23 10:02 xq52301nzdm

为什么看上去不对,答案却是对的? image 分开操作 意思明明是 text 和 json 都必须有???

因为在typescript中有一个条件分布的概念,条件分布的规则主要是在泛型、类型参数等地方应用的,所以当你把text | json 当做泛型参数传入某个类型的时候,其实typescript会把它每个成员拆解开来进行条件判断,也就是KeysType第一次是text,第二次是json

ResponseState200 avatar Dec 28 '23 07:12 ResponseState200

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Dec 28 '23 07:12 xq52301nzdm

RequireAtLeastOne 返回的是联合类型

{
  text: () => string;
  json?: () => string;
  secure?: boolean;  
} | {
  text?: () => string;
  json: () => string;
  secure?: boolean;  
}```

lfzs avatar Apr 11 '24 08:04 lfzs

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

xq52301nzdm avatar Apr 11 '24 08:04 xq52301nzdm