yunliuyan / type-challenges

typescript-challenges
0 stars 2 forks source link

00006-extreme-readonly-keys #6

Open yunliuyan opened 1 year ago

yunliuyan commented 1 year ago

获取只读字段 地狱 #utils #object-keys

by Anthony Fu @antfu

接受挑战    English 日本語

实现泛型GetReadonlyKeys<T>GetReadonlyKeys<T>返回由对象 T 所有只读属性的键组成的联合类型。

例如

interface Todo {
  readonly title: string
  readonly description: string
  completed: boolean
}

type Keys = GetReadonlyKeys<Todo> // expected to be "title" | "description"

测试案例:

import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<'title', GetReadonlyKeys<Todo1>>>,
  Expect<Equal<'title' | 'description', GetReadonlyKeys<Todo2>>>,
]

interface Todo1 {
  readonly title: string
  description: string
  completed: boolean
}

interface Todo2 {
  readonly title: string
  readonly description: string
  completed?: boolean
}

返回首页 分享你的解答 查看解答
yunliuyan commented 1 year ago

思路

遍历对象key值,若对应的属性继承了Readonly则取当前值。

代码实现:

 type GetReadonlyKeys<T> = {
    [key in keyof T ]-?: Equal<Pick<T, key>, Readonly<Pick<T, key>>> extends true ? key : never
  }[keyof T];

类型映射变体

wudu8 commented 1 year ago
export type ExtremeReadOnly<T> = {
    [K in keyof T]-?: (<U>() => U extends { -readonly [P in K]: T[K] } ? 1 : 2) extends <U>() => U extends { [P in K]: T[K] } ? 1 : 2 ? never : K;
}[keyof T];

interface ExtremeReadonlyType {
    readonly name: string;
    address: boolean;
    readonly meta: {
        description: string;
    };
    description: string;
}

type extremeReadonlyTest = ExtremeReadOnly<ExtremeReadonlyType>;

const extremeReadonlyTest1: extremeReadonlyTest = "name"; // Ok
const extremeReadonlyTest2: extremeReadonlyTest = "address"; // Error: Type '"description"' is not assignable to type 'extremeReadonlyTest'.ts(2322)
const extremeReadonlyTest3: extremeReadonlyTest = "meta"; // Ok
const extremeReadonlyTest4: extremeReadonlyTest = "description"; // Error: Type '"description"' is not assignable to type 'extremeReadonlyTest'.ts(2322)
Janice-Fan commented 1 year ago

type Equal<X, Y> = (() => T extends X ? 1 : 2) extends (() => T extends Y ? 1 : 2) ? true : false;

type GetReadonlyKeys = keyof {

}

interface Todo { readonly title: string readonly description: string completed: boolean }

type Keys = GetReadonlyKeys // expected to be "title" | "description"

const test: Keys = 'title';

liangchengv commented 1 year ago
type GetReadonlyKeys<T> = {
  [P in keyof T as Equal<Pick<T, P>, Readonly<Pick<T, P>>> extends true ? P: never]: T[P]
}[keyof T];
Naparte commented 1 year ago
type Equals<X, Y> =
    (<T>() => (T extends X ? 1 : 2)) extends
    (<Z>() => (Z extends Y ? 1 : 2))
    ? true
    : false;
// https://juejin.cn/post/7078208046283014158

type GetReadonlyKeys<T> = { [key in keyof T]: Equals<Pick<T, key>, Readonly<Pick<T, key>>> extends true ? key : never }[keyof T]

interface Todo6 {
    readonly title: string
    readonly description: string
    completed: boolean
}

type Keys = GetReadonlyKeys<Todo6> // expected to be "title" | "description"