My project is structured like this:
MyScript.ps1
classes\
Car.ps1
Tesla.ps1
Car.ps1 is the base class of Tesla.ps1. I attempt to define Tesla like this in Tesla.ps1:
. "$PSScriptRoot\Car.ps1"
class Tesla : Car
{
}
MyScript.ps1 needs to use the Tesla class, but shouldn't need to know that it inherits from Car.
. "$PSScriptRoot\classes\Tesla.ps1"
$tesla = [Tesla]::new()
Dot sourcing to classes\Tesla.ps1
works fine, but this error is thrown from the Tesla file:
Unable to find type [Car]
If I import all the files in the correct order in MyScript.ps1, it works fine. Example:
. "$PSScriptRoot\classes\Car.ps1"
. "$PSScriptRoot\classes\Tesla.ps1"
$tesla = [Tesla]::new()
This is cumbersome, especially as the complexity grows. Am I dot sourcing incorrectly? Is there a better way to import a custom PowerShell class using a relative path that isn't in the PSModulePath?
PetSerAl, as countless times before, has provided the crucial pointers in a terse comment on the question:
Due to your use of classes, you must use modules and import them with using module
statements in order to use them.
Specifically, in order to reference a type (class) in a class
definition, that type must be known to PowerShell at parse time.
In your example, in order to derive Tesla
from class Car
, type Car
must be known to PowerShell when the script is parsed, before execution begins - that's why it is too late to try to import Car
by dot-sourcing its containing script (. "$PSScriptRoot\Car.ps1"
)
PowerShell knows about referenced types at parse time in one of two ways:
If the type is already loaded into the current PowerShell session
Via a using module
statement that references a module in which the type (class) is defined (note that there's also a using assembly
statement for loading types from DLLs, and using namespace
to enable referring to types by their mere names).
Import-Module
cmdlet does not work in this case, because it executes at runtime.Therefore, as PetSerAl suggests:
Store your classes in module files (stand-alone *.psm1
files in the simplest case)
Then use using module
to have the Tesla
module import the Car
module and the MyScript.ps1
script import the Tesla
module, using paths that are relative to the enclosing script / module's location.
MyScript.ps1
classes\
Car.psm1 # Note the .psm1 extension
Tesla.psm1 # Ditto
Car.psm1
:
class Car {}
Tesla.psm1
:
using module .\Car.psm1
class Tesla : Car {}
MyScript.ps1
:
using module .\classes\Tesla.psm1
$tesla = [Tesla]::new()