建议
🔍 搜索词
缩小全球类型Assert函数类型 predicate
✅ 可实现性检查清单
我的建议满足以下准则:
- 这不会对现有的TypeScript/JavaScript代码造成破坏性的改变
- 这不会改变现有JavaScript代码的运行时行为
- 这可以在不根据表达式的类型发出不同的JS的情况下实现
- 这不是一个运行时特性(例如库功能、JavaScript输出的非ECMAScript语法、JS的新语法糖等)
- 这个特性将与TypeScript's Design Goals的其他部分一致。
⭐ 建议
允许Assert函数(和类型 predicate 函数)做以下事情:
- 更改函数参数之外的值的类型
- 扩展全局声明的接口
📃 动机示例
对于原型扩展等场景非常有用,因为在这种情况下,你不得不定义新属性的类型两次:
declare global {
interface String {
foo: (value: string) => string
}
}
Object.defineProperty(String.prototype, 'foo', {
value: (value: string) => value,
enumerable: false,
})
相反,你可以定义一个 Package 函数来完成这两件事:
export function defineProperty<T, Key extends string | number | symbol, Value>(
object: T,
key: Key,
value: Value
): asserts InstanceType<T> /* this can now be either a value or a type*/ is T & { [K in Key]: Value } {
Object.defineProperty(object, key, { value, enumerable: false })
}
defineProperty(String.prototype, 'foo', (value: string) => value);
['hi'].foo('value')
💻 使用案例
对于cypress这样的工具,当你需要添加新函数时,你不得不定义它们的签名两次(参见https://docs.cypress.io/api/cypress-api/custom-commands)
declare global {
interface Chainable {
foo(bar: number): string
}
}
Cypress.Commands.add('foo', (bar: number) => "foo")
相反,你可以使用Assert函数同时定义两者。
//(pseudocode, the actual function signature to make this accurate to how commands work in cypress is like 60 lines long)
function addCommand<Name extends string, Func extends (...args: any[]) => any>(
name: Name,
func: Func
): asserts Chainable is Chainable & { [Key in Name]: Func } /* new syntax */ {
Cypress.Commands.add(name, func)
}
//add the new command
addCommand(cy, 'foo', (bar: number) => "foo")
//use the new command
cy.foo(1)
目前,你可以这样做:
function addCommand<Name extends string, Func extends (...args: any[]) => any>(
_cy: typeof cy, //need to pass the unused cy object in order for the function to be able to change its type
name: Name,
func: Func
): asserts _cy is typeof _cy & { [Key in Name]: Func } {
Cypress.Commands.add(name, func)
}
当前方法的问题:
- 你必须无谓地将全局
cy
对象传递给函数,否则其类型无法Assert - 只有
cy
Chainable
示例被缩小,而不是Chainable
接口本身,这意味着如果你将其链接到之前的命令上,你不能使用这个新方法。
cy.get('foo').foo(1) //error: property 'foo' doesn't exist on Chainable
1条答案
按热度按时间qlzsbp2j1#
这是一个使用案例:
我有一个名为
_expect
的函数,它返回{ toHaveProperty() }
。这样我就可以执行类似_expect(globalThis).toHaveProperty('hasOwnProperty')
、的操作。但是这里有一个错误,因为
toHaveProperty
没有object
作为参数。参考链接:https://www.typescriptlang.org/play?#code/MYewdgzgLgBA+gUwB4AcHFgXhgChAIwCt0oAuGAQzAE8BKGTAPhgG8BYAKBm4CcEoArjzCtO3cTCggAEhQBuCAAo8QaHlGoAeACoxkUBGAAmEGMtUJ11ANIJqjHHwCOAgJZ8j5tRvLba5CggISyhTAmIMGFdTACV0EB4jHQAaGAEwAGswEAB3MGZ2LgkJVwAzXABCRwQXdwRPFW9qKJFwklp6QuLuyQALFRyYbWo0AFEeFR4cAAM42o8YFEaQ5oASFgBlKB5XMABzavn6rxXaAF8o0wBbaIhdvenaMR6YM+eJN6LXzjOgA