semlinker/awesome-typescript

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

Opened this issue · 32 comments

实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性。具体的使用示例如下所示:

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = // 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

提示:该题目有多种解法,感兴趣小伙伴可以自行尝试一下。
请在下面评论你的答案。

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = NonNullable<{
  [P in keyof T]: undefined extends T[P] ? P : never
}[keyof T]>

type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = Exclude<
  {
    [K in keyof T]: undefined extends T[K] ? K : never;
  }[keyof T],
  undefined
>;

type PersonOptionalKeys = OptionalKeys<Person>; // "from" | "speak
type Undefined<T> = { [P in keyof T]: P extends undefined ? T[P] : never }

type ConditionPickKey<T, Condition> = {
  [Key in keyof T]: T[Key] extends Condition ? Key : never
}

type OptionalKeys<T> = Exclude<keyof T, NonNullable<ConditionPickKey<Undefined<T>, never>[keyof T]>>

interface Something {
  a: undefined  // special test case
  b: string
  phone?: string
}
type AllOptionalKeys = OptionalKeys<Something> // "phone"
// 启用 --strictNullchecks 即在tsconfig.js中设置"strictNullChecks": true
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = {
    [p in keyof T]: undefined extends T[p] ? p : never
}[keyof T]// 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

@mingzhans 如果没有设置 "strictNullChecks": true 配置项的话,可以想一想应该如何处理。

type OptionalKeys<T> = Exclude<{
    [P in keyof T]: T extends T[P] ? never : T[P]
}[keyof T], undefined>
ln0y commented
  type OptionalKeys<T> = keyof {
    [P in keyof T as undefined extends T[P] ? P : never]: T[P]
  }
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = NonNullable<
  {
    [K in keyof T]: undefined extends T[K] ? K : never;
  }[keyof T]
>;

type PersonOptionalKeys = OptionalKeys<Person>; // "from" | "speak"
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};
type partical<T> = {
  [x in keyof T]?: T[x]
}

type OptionalKeys<T> = 
keyof
{
  [x in keyof T as partical<T>[x] extends T[x] ? x : never]: T[x] 
}

type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
winfa commented
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = // 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
type OptionalKeys<T> = {
  [P in keyof T]: (undefined extends T[P] ? P : never)
}[keyof T] & keyof T
// 实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性。具体的使用示例如下所示:

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type IsEqual<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;

type OptionalKeys<T> = NonNullable<{
  [k in keyof T]: IsEqual<Pick<T, k>, Partial<Pick<T, k>>> extends true ? k : never;
}[keyof T]>

type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

解题思路: 本题在严格模式下较难,利用在迭代的过程中利用 Pick 构造单一属性的类型,之后去和只读的去全IsEqual的比较。相同则证明是只读类型,保留,否则返回never,最后利用 [keyof T] 读取所有的值的联合类型。
我没考清楚为啥会有undefined,最后通过NonNullable去掉undefined, null这些空值

Mrlgm commented
type Person = {
    id: string;
    name: string;
    age: number;
    from?: string;
    speak?: string;
};

type OptionalKeys<T> = {
    [P in keyof T]-?: undefined extends T[P] ? P : never
}[keyof T]

// 测试用例
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

image
你们很多答案都是这个结果,只有我是这样吗

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = {
  [P in keyof T]: Pick<T, P> extends Required<Pick<T, P>> ? never : P
}[keyof T]

type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

上面感觉没一个对的

type OptionalKeys<T> = {
  [P in keyof T]: undefined extends T[P] ? P : never
}[keyof T]

这样也没有报错

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = {
  [K in keyof T]: {} extends Pick<T, K> ? K : never;
}[keyof T];

type PersonOptionalKeys = OptionalKeys<Person>; // "from" | "speak"

export default {}

// 实现一个 OptionalKeys 工具类型,用来获取对象类型中声明的可选属性。具体的使用示例如下所示:
type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};

type OptionalKeys = NonNullable<{
[P in keyof T]: undefined extends T[P] ? P : never
}[keyof T]>
type PersonOptionalKeys = OptionalKeys // "from" | "speak"

`
type OptionalKeys = {
[P in keyof T]: {} extends Pick<T, P> ? P : never;
}[keyof T]

`

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = { [P in keyof T]-?: undefined extends T[P] ? P : never }[keyof T];
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = {
  [P in keyof T]: Pick<T, P> extends Required<Pick<T, P>> ? never : P
}[keyof T]

type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

上面感觉没一个对的

我用你的这个得到的是:
"from" | "speak" | undefined

type Person = {
...
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

上面感觉没一个对的

我这显示也是这样。。用在线的 https://typescript-play.js.org/ 会不一样结果。不知道为什么。


tsc --init
然后就正常了。可能有些配置的关系

type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};

type OptionalKeys = {
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];

type Filter = T extends undefined ? never : T;

type PersonOptionalKeys = Filter<OptionalKeys> // "from" | "speak"

type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};


type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

type OptionalKeys<T> = keyof {
  [K in keyof T as T[K] extends Exclude<T[K], undefined> ? never : K]: T[K]
}

补充:

//判断条件放在右侧里
type OptionalKeys1<T> = Exclude<{
   [P in keyof T]: undefined extends T[P] ? P : never
}[keyof T], undefined>
// 第一版的改造,NonNullable工具类型去掉联合类型中的undefined|null
type OptionalKeys2<T> = keyof{
  [K in keyof T as T[K] extends NonNullable<T[K]> ? never : K]: T[K]
}

type Person = {
id: undefined;
name: string;
age: number;
from?: string;
speak?: string;
};

type C = { name:string,age:string} extends {
name:string
}?true:false;
type OptionalKeys = keyof {
[K in keyof T as {} extends Pick<T,K> ? K: never ]: T[K]
} // 你的实现代码
type PersonOptionalKeys = OptionalKeys // "from" | "speak"

dentm commented
type Person = {
  id: string;
  name: string;
  age: number;
  from?: string;
  speak?: string;
};

type OptionalKeys<T> = keyof {
    [P in keyof T as undefined extends T[P] ? P : never] :T[P]
}
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
type Person = {
    id: string;
    name: string;
    age: number;
    from?: string;
    speak?: string;
};
type OptionalKeys<T> = NonNullable<
{
    [K in keyof T]: T extends Record<K, T[K]> ? never : K;
}[keyof T]>// 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

就没有人问为啥有个 Undefined 吗?,能有人回答一下吗?

type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};
type OptionalKeys <T> ={[K in keyof T]: T extends Record<K, T[K]> ? never : K}[keyof T];
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"

type IsEqual<T, U> = [T] extends [U] ? [U] extends [T] ? true : false : false;

type OptionalKeys<T> = {
  [k in keyof T]: IsEqual<Pick<T, k>, Partial<Pick<T, k>>> extends true ? k : never;
}[keyof T]

这种在设置 strictNullChecks = false时也是可以的

type OptionalKeys<T> = keyof {
	[P in keyof T as T[P] extends Required<T>[P] ? never : P]: P;
};

type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};

type OptionalKeys = {
[P in keyof T]-?: undefined extends T[P] ? P : never;
}[keyof T];

type PersonOptionalKeys = OptionalKeys; // "from" | "speak"

就没有人问为啥有个 Undefined 吗?,能有人回答一下吗?

type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};

type OptionalKeys = {
[P in keyof T]-?: undefined extends T[P] ? P : never;
}[keyof T];

type PersonOptionalKeys = OptionalKeys; // "from" | "speak"