对象的断言属性是非空的,使用函数的参数



我想要实现一个类型安全的API,它接受一个泛型,并且只有在满足条件时才执行:

const doSomethingWith = (data: unknown) => data
const fn = <T extends unknown>(data: T, opts: { if: boolean }): void => {
if (opts.if == false) return;
doSomethingWith(data)
}

在这种情况下,如何使opts.if成为有效的谓词?考虑以下情况:

type Data = { abc: string }
// should be valid - checks to make sure data.abc is non-null
const data: Partial<Data> = {}
fn<Data>(data, { if: data.abc != null  })
// should be valid - all data is given
const data2: Data = { abc: '123' }
fn<Data>(data2)
// should be invalid - no null check occurs
const data3: Partial<Data> = {}
fn<Data>(data3)

打字游戏场。

我会通过将opts.if参数作为一个函数而不是静态boolean值来实现这一点。

通过这种方式,使谓词和作为第一个参数传递的数据类型匹配非常简单。

const doSomethingWith = <T>(data: T) => data
const fn = <T>(data: T, opts?: { if: (t: T) => boolean }): void => {
if (opts?.if(data) === false) return;
doSomethingWith(data)
}
type Data = { abc: string }
const data: Partial<Data> = {}
const data2: Data = { abc: '123' }
const data3: Partial<Data> = {}

fn(data, { if: d => d.abc != null  }) // OK, valid predicate
fn(data2) // OK, no predicate
fn(data3, { if: d => d.foobar != null  }) // NOT OK, predicte doesn't match data

游乐场

我的第一次尝试太复杂了。但是,当再次阅读你的问题时,我认为我们可以把它变得非常简单。

解决方案

您可以使用函数重载来要求参数有效。

function fn<T>(data: Partial<T>, opts: { if: boolean }): void
function fn<T>(data: T): void
function fn<T>(data: T, opts?: { if: boolean }): void {
if (opts?.if == false) return;
doSomethingWith(data);
}

第三行fn是实现签名,前两行是必须匹配的函数签名。


说明

您的三个示例都期望doSomethingWith将使用一个完整的Data对象进行调用。

如果data变量不完整,则必须提供opts。第一个过载签名允许Partial<Data>,但需要opts:

function fn<T>(data: Partial<T>, opts: { if: boolean }): void

与示例#1:相匹配

// should be valid - checks to make sure data.abc is non-null
const data: Partial<Data> = {}
fn<Data>(data, { if: data.abc != null  })

如果我们已经知道data变量是完整的,那么您不需要提供任何opts。这就是这个签名:

function fn<T>(data: T): void

与示例#2匹配:

// should be valid - all data is given
const data2: Data = { abc: '123' }
fn<Data>(data2)

第三个示例失败了,因为它与重载的函数签名都不匹配。由于我们还没有提供opts参数,所以第一个参数必须是一个完整的Data对象。但是data3Partial<Data>,所以我们得到一个错误:

// should be invalid - no null check occurs
const data3: Partial<Data> = {}
fn<Data>(data3)
Argument of type 'Partial<Data>' is not assignable to parameter of type 'Data'.
Types of property 'abc' are incompatible.
Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.(2345)

TypeScript游乐场链接

最新更新