fix(napi-derive-backend): Option value should produce optional types

This commit is contained in:
LongYinan 2021-11-16 13:02:40 +08:00
parent 0e0bfb1c0a
commit b4a8cadb21
No known key found for this signature in database
GPG key ID: C3666B7FC82ADAD7
5 changed files with 61 additions and 34 deletions

View file

@ -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),
}
}

View file

@ -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 {

View file

@ -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());
}

View file

@ -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

View file

@ -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]