验证 属性 存在的 Typescript class 吸气剂
Typescript class getters that validate property presence
我很喜欢一种新的(对我而言)模式,即从 api 响应中创建数据模型 类,因为它们可以具有可重用的派生逻辑,而不必从 api.
我的问题是有多个吸气剂,我想在相互引用(或实际上在其他文件中使用)时断言 属性 的存在,但没有,所以我想知道我在做什么丢失或更好的解决方案是:
type TodoResponse = {
userId: number;
id: number;
title: string;
completed: boolean;
authors?: string[];
}
class Todo implements TodoResponse {
userId!: number;
id!: number;
title!: string;
completed!: boolean;
authors?: string[];
constructor(todo: TodoResponse) {
Object.assign(this, todo);
}
get hasAuthors(): boolean {
return Boolean(this.authors?.length);
}
get firstAuthor(): string | void {
if (this.hasAuthors) return this.authors[0]
// errors with: Object is possibly 'undefined'
}
get firstAuthor(): string | void {
if (this.authors?.length) return this.authors[0]
// this works but it feels redundant to duplicate logic from other getters
}
}
你想要的是检查 todo.hasAuthors
作为 类型保护 可以用于 narrow [=14] 类型=] 到已知包含已定义 authors
属性 的内容。不幸的是,TypeScript 目前没有办法实现这一点。
首先,classes 无法实现 discriminated union types; otherwise you could have Todo
be assignable to {hasAuthors: true, authors: string[]} | {hasAuthors: false, authors?: undefined}
. You could maybe use type assertions 让 Todo
从 外部 看起来像这样:
interface TodoWithAuthors extends TodoResponse {
hasAuthors: true,
authors: string[],
firstAuthor: string
}
interface TodoWithoutAuthors extends TodoResponse {
hasAuthors: false,
authors?: undefined,
firstAuthor: void
}
type Todo = TodoWithAuthors | TodoWithoutAuthors;
const Todo = class Todo implements TodoResponse {
/* snip, your original impl goes here */
} as new (todo: TodoResponse) => Todo;
const todo = new Todo({
id: 1, userId: 2, title: "",
completed: false, authors: Math.random() > 0.99 ? undefined : ["a", "b"]
});
if (todo.hasAuthors) {
// no compiler errors here
console.log(todo.authors.join(", ")) // a, b
console.log(todo.firstAuthor.toUpperCase()) // A
}
但是从内部,编译器看不出 this.hasAuthors
对 this.authors
有任何影响。所以这不会以你想要的方式帮助你。
TypeScript 确实有 user-defined type guard functions and methods 的概念,你可以在其中调用 boolean
-returning 函数或方法,它会在其中一个上充当类型保护输入参数(或方法的 this
上下文)。所以如果 hasAuthors
是一个方法而不是 getter,你可以这样做:
class Todo implements TodoResponse {
userId!: number;
id!: number;
title!: string;
completed!: boolean;
authors?: string[];
constructor(todo: TodoResponse) {
Object.assign(this, todo);
}
hasAuthors(): this is { authors: string[], firstAuthor: string } {
return Boolean(this.authors?.length);
}
get firstAuthor(): string | void {
if (this.hasAuthors()) return this.authors[0] // okay
}
}
通过将 hasAuthors()
的 return 类型注释为 this is { authors: string[], firstAuthor: string }
,我是说 obj.hasAuthors()
的 true
结果会缩小 obj.hasAuthors()
的类型obj
到具有定义的 string[]
属性 的东西(以及定义的 firstAuthor
属性)。这在 firstAuthor()
的实现中起作用。它也适用于 class:
const todo = new Todo({
id: 1, userId: 2, title: "",
completed: false, authors: Math.random() > 0.99 ? undefined : ["a", "b"]
});
if (todo.hasAuthors()) {
// no compiler errors here
console.log(todo.authors.join(", ")) // a, b
console.log(todo.firstAuthor.toUpperCase()) // A
}
所以,太好了。不幸的是,没有类似的 user-defined 类型保护 属性 或 访问器方法 功能。 microsoft/TypeScript#43368 有一个开放的功能请求,其状态当前为“等待更多反馈”。这意味着他们可能甚至不会考虑实施这样的功能,除非他们从社区听到一些引人注目的用例。如果你想看到这种情况发生,你可能会考虑解决这个问题,给它一个 ,并解释为什么这会比当前的选项更好。但即使你这样做了,也不知道什么时候甚至是否可以使用这样的功能。
所以现在你有点卡住了。要么使用 if (this.authors?.length) return this.authors[0]
形式的冗余类型保护,要么使用像 if (this.hasAuthors) return this.authors![0]
这样的类型断言,要么重构你想要的实现。
我很喜欢一种新的(对我而言)模式,即从 api 响应中创建数据模型 类,因为它们可以具有可重用的派生逻辑,而不必从 api.
我的问题是有多个吸气剂,我想在相互引用(或实际上在其他文件中使用)时断言 属性 的存在,但没有,所以我想知道我在做什么丢失或更好的解决方案是:
type TodoResponse = {
userId: number;
id: number;
title: string;
completed: boolean;
authors?: string[];
}
class Todo implements TodoResponse {
userId!: number;
id!: number;
title!: string;
completed!: boolean;
authors?: string[];
constructor(todo: TodoResponse) {
Object.assign(this, todo);
}
get hasAuthors(): boolean {
return Boolean(this.authors?.length);
}
get firstAuthor(): string | void {
if (this.hasAuthors) return this.authors[0]
// errors with: Object is possibly 'undefined'
}
get firstAuthor(): string | void {
if (this.authors?.length) return this.authors[0]
// this works but it feels redundant to duplicate logic from other getters
}
}
你想要的是检查 todo.hasAuthors
作为 类型保护 可以用于 narrow [=14] 类型=] 到已知包含已定义 authors
属性 的内容。不幸的是,TypeScript 目前没有办法实现这一点。
首先,classes 无法实现 discriminated union types; otherwise you could have Todo
be assignable to {hasAuthors: true, authors: string[]} | {hasAuthors: false, authors?: undefined}
. You could maybe use type assertions 让 Todo
从 外部 看起来像这样:
interface TodoWithAuthors extends TodoResponse {
hasAuthors: true,
authors: string[],
firstAuthor: string
}
interface TodoWithoutAuthors extends TodoResponse {
hasAuthors: false,
authors?: undefined,
firstAuthor: void
}
type Todo = TodoWithAuthors | TodoWithoutAuthors;
const Todo = class Todo implements TodoResponse {
/* snip, your original impl goes here */
} as new (todo: TodoResponse) => Todo;
const todo = new Todo({
id: 1, userId: 2, title: "",
completed: false, authors: Math.random() > 0.99 ? undefined : ["a", "b"]
});
if (todo.hasAuthors) {
// no compiler errors here
console.log(todo.authors.join(", ")) // a, b
console.log(todo.firstAuthor.toUpperCase()) // A
}
但是从内部,编译器看不出 this.hasAuthors
对 this.authors
有任何影响。所以这不会以你想要的方式帮助你。
TypeScript 确实有 user-defined type guard functions and methods 的概念,你可以在其中调用 boolean
-returning 函数或方法,它会在其中一个上充当类型保护输入参数(或方法的 this
上下文)。所以如果 hasAuthors
是一个方法而不是 getter,你可以这样做:
class Todo implements TodoResponse {
userId!: number;
id!: number;
title!: string;
completed!: boolean;
authors?: string[];
constructor(todo: TodoResponse) {
Object.assign(this, todo);
}
hasAuthors(): this is { authors: string[], firstAuthor: string } {
return Boolean(this.authors?.length);
}
get firstAuthor(): string | void {
if (this.hasAuthors()) return this.authors[0] // okay
}
}
通过将 hasAuthors()
的 return 类型注释为 this is { authors: string[], firstAuthor: string }
,我是说 obj.hasAuthors()
的 true
结果会缩小 obj.hasAuthors()
的类型obj
到具有定义的 string[]
属性 的东西(以及定义的 firstAuthor
属性)。这在 firstAuthor()
的实现中起作用。它也适用于 class:
const todo = new Todo({
id: 1, userId: 2, title: "",
completed: false, authors: Math.random() > 0.99 ? undefined : ["a", "b"]
});
if (todo.hasAuthors()) {
// no compiler errors here
console.log(todo.authors.join(", ")) // a, b
console.log(todo.firstAuthor.toUpperCase()) // A
}
所以,太好了。不幸的是,没有类似的 user-defined 类型保护 属性 或 访问器方法 功能。 microsoft/TypeScript#43368 有一个开放的功能请求,其状态当前为“等待更多反馈”。这意味着他们可能甚至不会考虑实施这样的功能,除非他们从社区听到一些引人注目的用例。如果你想看到这种情况发生,你可能会考虑解决这个问题,给它一个 ,并解释为什么这会比当前的选项更好。但即使你这样做了,也不知道什么时候甚至是否可以使用这样的功能。
所以现在你有点卡住了。要么使用 if (this.authors?.length) return this.authors[0]
形式的冗余类型保护,要么使用像 if (this.hasAuthors) return this.authors![0]
这样的类型断言,要么重构你想要的实现。