fix(napi-derive-backend): Option value should produce optional types
This commit is contained in:
parent
0e0bfb1c0a
commit
b4a8cadb21
5 changed files with 61 additions and 34 deletions
|
@ -69,7 +69,6 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Buffer", "Buffer"),
|
||||
// TODO: Vec<u8> should be Buffer, now is Array<number>
|
||||
("Vec", "Array<{}>"),
|
||||
("Option", "{} | null"),
|
||||
("Result", "Error | {}"),
|
||||
("Either", "{} | {}"),
|
||||
("Either3", "{} | {} | {}"),
|
||||
|
@ -106,21 +105,24 @@ fn fill_ty(template: &str, args: Vec<String>) -> String {
|
|||
ret
|
||||
}
|
||||
|
||||
pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool) -> String {
|
||||
pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool) -> (String, bool) {
|
||||
match ty {
|
||||
Type::Reference(r) => ty_to_ts_type(&r.elem, is_return_ty),
|
||||
Type::Tuple(tuple) => {
|
||||
if tuple.elems.is_empty() {
|
||||
"undefined".to_owned()
|
||||
("undefined".to_owned(), false)
|
||||
} else {
|
||||
format!(
|
||||
"[{}]",
|
||||
tuple
|
||||
.elems
|
||||
.iter()
|
||||
.map(|elem| ty_to_ts_type(elem, false))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
(
|
||||
format!(
|
||||
"[{}]",
|
||||
tuple
|
||||
.elems
|
||||
.iter()
|
||||
.map(|elem| ty_to_ts_type(elem, false).0)
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -144,36 +146,46 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool) -> String {
|
|||
|
||||
if rust_ty == "Result" && is_return_ty {
|
||||
ts_ty = Some(args.first().unwrap().to_owned());
|
||||
} else if rust_ty == "Option" {
|
||||
ts_ty = args
|
||||
.first()
|
||||
.map(|(arg, _)| (format!("{} | undefined | null", arg), true));
|
||||
} else if rust_ty == "AsyncTask" {
|
||||
ts_ty = r#struct::TASK_STRUCTS.with(|t| {
|
||||
let output_type = args.first().unwrap().to_owned();
|
||||
let (output_type, _) = args.first().unwrap().to_owned();
|
||||
if let Some(o) = t.borrow().get(&output_type) {
|
||||
Some(format!("Promise<{}>", o))
|
||||
Some((format!("Promise<{}>", o), false))
|
||||
} else {
|
||||
Some("Promise<unknown>".to_owned())
|
||||
Some(("Promise<unknown>".to_owned(), false))
|
||||
}
|
||||
});
|
||||
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
|
||||
if known_ty.contains("{}") {
|
||||
ts_ty = Some(fill_ty(known_ty, args));
|
||||
ts_ty = Some((
|
||||
fill_ty(known_ty, args.into_iter().map(|(arg, _)| arg).collect()),
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
ts_ty = Some(known_ty.to_owned());
|
||||
ts_ty = Some((known_ty.to_owned(), false));
|
||||
}
|
||||
} else if let Some(t) = crate::typegen::r#struct::CLASS_STRUCTS
|
||||
.with(|c| c.borrow_mut().get(rust_ty.as_str()).cloned())
|
||||
{
|
||||
ts_ty = Some(t);
|
||||
ts_ty = Some((t, false));
|
||||
} else if rust_ty == "Promise" {
|
||||
ts_ty = Some(format!("Promise<{}>", args.first().unwrap()));
|
||||
ts_ty = Some((
|
||||
format!("Promise<{}>", args.first().map(|(arg, _)| arg).unwrap()),
|
||||
false,
|
||||
));
|
||||
} else {
|
||||
// there should be runtime registered type in else
|
||||
ts_ty = Some(rust_ty);
|
||||
ts_ty = Some((rust_ty, false));
|
||||
}
|
||||
}
|
||||
|
||||
ts_ty.unwrap_or_else(|| "any".to_owned())
|
||||
ts_ty.unwrap_or_else(|| ("any".to_owned(), false))
|
||||
}
|
||||
Type::Group(g) => ty_to_ts_type(&g.elem, is_return_ty),
|
||||
_ => "any".to_owned(),
|
||||
_ => ("any".to_owned(), false),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,11 +30,18 @@ fn gen_callback_type(callback: &CallbackArg) -> String {
|
|||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, arg)| { format!("arg{}: {}", i, ty_to_ts_type(arg, false)) })
|
||||
.map(|(i, arg)| {
|
||||
let (arg, is_optional) = ty_to_ts_type(arg, false);
|
||||
if is_optional {
|
||||
format!("arg{}?: {}", i, arg)
|
||||
} else {
|
||||
format!("arg{}: {}", i, arg)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
ret = match &callback.ret {
|
||||
Some(ty) => ty_to_ts_type(ty, true),
|
||||
Some(ty) => ty_to_ts_type(ty, true).0,
|
||||
None => "void".to_owned(),
|
||||
}
|
||||
)
|
||||
|
@ -56,8 +63,9 @@ impl NapiFn {
|
|||
i.mutability = None;
|
||||
}
|
||||
let mut arg = path.pat.to_token_stream().to_string().to_case(Case::Camel);
|
||||
arg.push_str(": ");
|
||||
arg.push_str(&ty_to_ts_type(&path.ty, false));
|
||||
let (ts_arg, is_optional) = ty_to_ts_type(&path.ty, false);
|
||||
arg.push_str(if is_optional { "?: " } else { ": " });
|
||||
arg.push_str(&ts_arg);
|
||||
|
||||
Some(arg)
|
||||
}
|
||||
|
@ -100,7 +108,7 @@ impl NapiFn {
|
|||
.unwrap_or_else(|| "".to_owned()),
|
||||
_ => {
|
||||
let ret = if let Some(ret) = &self.ret {
|
||||
let ts_type = ty_to_ts_type(ret, true);
|
||||
let (ts_type, _) = ty_to_ts_type(ret, true);
|
||||
if ts_type == "undefined" {
|
||||
"void".to_owned()
|
||||
} else {
|
||||
|
|
|
@ -32,7 +32,7 @@ impl ToTypeDef for NapiImpl {
|
|||
if let Some(output_type) = &self.task_output_type {
|
||||
TASK_STRUCTS.with(|t| {
|
||||
t.borrow_mut()
|
||||
.insert(self.js_name.clone(), ty_to_ts_type(output_type, false));
|
||||
.insert(self.js_name.clone(), ty_to_ts_type(output_type, false).0);
|
||||
});
|
||||
}
|
||||
TypeDef {
|
||||
|
@ -61,7 +61,9 @@ impl NapiStruct {
|
|||
if !f.setter {
|
||||
field_str.push_str("readonly ")
|
||||
}
|
||||
let arg = format!("{}: {}", &f.js_name, ty_to_ts_type(&f.ty, false));
|
||||
let (arg, is_optional) = ty_to_ts_type(&f.ty, false);
|
||||
let sep = if is_optional { "?" } else { "" };
|
||||
let arg = format!("{}{}: {}", &f.js_name, sep, arg);
|
||||
if self.kind == NapiStructKind::Constructor {
|
||||
ctor_args.push(arg.clone());
|
||||
}
|
||||
|
|
10
examples/napi/index.d.ts
vendored
10
examples/napi/index.d.ts
vendored
|
@ -7,7 +7,7 @@ export function bigintAdd(a: BigInt, b: BigInt): BigInt
|
|||
export function createBigInt(): BigInt
|
||||
export function createBigIntI64(): BigInt
|
||||
export function getCwd(callback: (arg0: string) => void): void
|
||||
export function readFile(callback: (arg0: Error | undefined, arg1: string | null) => void): void
|
||||
export function readFile(callback: (arg0: Error | undefined, arg1?: string | undefined | null) => void): void
|
||||
export function eitherStringOrNumber(input: string | number): number
|
||||
export function returnEither(input: number): string | number
|
||||
export function either3(input: string | number | boolean): number
|
||||
|
@ -19,7 +19,7 @@ export enum Kind { Dog = 0, Cat = 1, Duck = 2 }
|
|||
export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }
|
||||
export function enumToI32(e: CustomNumEnum): number
|
||||
export function throwError(): void
|
||||
export function mapOption(val: number | null): number | null
|
||||
export function mapOption(val?: number | undefined | null): number | undefined | null
|
||||
export function add(a: number, b: number): number
|
||||
export function fibonacci(n: number): number
|
||||
export function listObjKeys(obj: object): Array<string>
|
||||
|
@ -31,8 +31,8 @@ export function asyncPlus100(p: Promise<number>): Promise<number>
|
|||
interface PackageJson {
|
||||
name: string
|
||||
version: string
|
||||
dependencies: Record<string, any> | null
|
||||
devDependencies: Record<string, any> | null
|
||||
dependencies?: Record<string, any> | undefined | null
|
||||
devDependencies?: Record<string, any> | undefined | null
|
||||
}
|
||||
export function readPackageJson(): PackageJson
|
||||
export function getPackageJsonName(packageJson: PackageJson): string
|
||||
|
@ -64,7 +64,7 @@ export class Blake2BKey {
|
|||
|
||||
}
|
||||
export class Context {
|
||||
|
||||
maybeNeed?: boolean | undefined | null
|
||||
constructor()
|
||||
static withData(data: string): Context
|
||||
method(): string
|
||||
|
|
|
@ -79,6 +79,7 @@ impl Blake2bKey {
|
|||
#[napi]
|
||||
pub struct Context {
|
||||
data: String,
|
||||
pub maybe_need: Option<bool>,
|
||||
}
|
||||
|
||||
// Test for return `napi::Result` and `Result`
|
||||
|
@ -88,12 +89,16 @@ impl Context {
|
|||
pub fn new() -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
data: "not empty".into(),
|
||||
maybe_need: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[napi(factory)]
|
||||
pub fn with_data(data: String) -> Result<Self> {
|
||||
Ok(Self { data })
|
||||
Ok(Self {
|
||||
data,
|
||||
maybe_need: Some(true),
|
||||
})
|
||||
}
|
||||
|
||||
#[napi]
|
||||
|
|
Loading…
Reference in a new issue