forked from extern/nushell
Relax type-check of key-less table
/record
(#10629)
# Description Relax typechecking of key-less `table`/`record` Assume that they are acceptable for more narrowly specified `table<...>`/`record<...>` where `...` specifies keys and potentially types for those keys/columns. This ensures that you can use commands that specify general return values statically with more specific input-/args-type requirements. Reduces the power of the type-check a bit but unlocks you to actually use the specific annotations in more places. Incompatibilities will only be raised if an output type declares specific columns/keys. Closes #9702 Supersedes #10594 as a simpler solution requiring no extra distinction. h/t @1kinoti, @NotLebedev # User-Facing Changes Now legal at type-check time ```nu def foo []: nothing -> table { [] } def foo []: nothing -> table<> { ls } def bar []: table<a:int,b:string> -> nothing {} foo | bar ``` # Tests + Formatting - 1 explicit test with specified relaxed return type passed to concrete expected input type - 1 test leveraging the general output type of a built-in command - 1 test wrapping a general built-in command and verifying the type inference in the function body
This commit is contained in:
parent
ff6c0fcb81
commit
e427c68731
@ -10,7 +10,9 @@ use nu_protocol::{
|
||||
pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
|
||||
// Structural subtyping
|
||||
let is_compatible = |expected: &[(String, Type)], found: &[(String, Type)]| {
|
||||
if expected.is_empty() {
|
||||
if expected.is_empty() || found.is_empty() {
|
||||
// We treat an incoming empty table/record type as compatible for typechecking purposes
|
||||
// It is the responsibility of the runtime to reject if necessary
|
||||
true
|
||||
} else if expected.len() > found.len() {
|
||||
false
|
||||
|
@ -87,6 +87,33 @@ fn record_subtyping_3() -> TestResult {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_subtyping_allows_general_record() -> TestResult {
|
||||
run_test(
|
||||
"def test []: record<name: string, age: int> -> string { $in; echo 'success' };
|
||||
def underspecified []: nothing -> record {{name:'Douglas', age:42}};
|
||||
underspecified | test",
|
||||
"success",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_subtyping_allows_record_after_general_command() -> TestResult {
|
||||
run_test(
|
||||
"def test []: record<name: string, age: int> -> string { $in; echo 'success' };
|
||||
{name:'Douglas', surname:'Adams', age:42} | select name age | test",
|
||||
"success",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_subtyping_allows_general_inner() -> TestResult {
|
||||
run_test(
|
||||
"def merge_records [other: record<bar: int>]: record<foo: string> -> record<foo: string, bar: int> { merge $other }",
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_into_load_env() -> TestResult {
|
||||
run_test(
|
||||
|
Loading…
Reference in New Issue
Block a user